Nathan Skrzypczak | cbb4e46 | 2021-10-13 12:40:42 +0200 | [diff] [blame] | 1 | // |
| 2 | // VPP C++ API example |
| 3 | // |
| 4 | // The uplink is the 1st interface (sw_if_index is 1) and is set |
| 5 | // to 10.10.10.10/24 The uplink gateway is 10.10.10.1 The IPsec tunnel is set |
| 6 | // between 10.10.10.10 and 10.20.20.20 The protected subnet is 192.168.0.0/24 |
| 7 | // VRF 1 is for uplink ingress |
| 8 | // VRF 2 is IPsec egress (clear -> cipher) |
| 9 | // VRF 3 is IPsec ingress (cipher -> clear) |
| 10 | // |
| 11 | // The following examples must be run with VPP in the following state: |
| 12 | // ip table add 1 # VRF 1 |
| 13 | // ip table add 2 # VRF 2 |
| 14 | // ip table add 3 # VRF 3 |
| 15 | // loop create # loop0 is used as uplink with sw_if_index=1 |
| 16 | // set int ip table loop0 1 # VRF 1 is ingress |
| 17 | // set int state loop0 up |
| 18 | // |
| 19 | // Then the API will configure VPP similar to this: |
| 20 | // # configure uplink address |
| 21 | // set int addr loop0 10.10.10.10/24 |
| 22 | // # create the IP-IP tunnel |
| 23 | // create ipip tunnel src 10.10.10.1 dst 10.20.20.20 outer-table-id 2 |
| 24 | // # use VRF-3 as IPsec ingress VRF (cipher -> clear) |
| 25 | // set int ip table ipip0 3 |
| 26 | // set int unnum ipip0 use loop0 |
| 27 | // set int state ipip0 up |
| 28 | // ipsec sa add 20 spi 200 crypto-key 01234567890123456789012345678901 |
| 29 | // crypto-alg aes-cbc-128 integ-key 01234567890123456789 integ-alg sha1-96 |
| 30 | // use-anti-replay udp-encap |
| 31 | // ipsec sa add 30 spi 300 crypto-key 01234567890123456789012345678901 |
| 32 | // crypto-alg aes-cbc-128 integ-key 01234567890123456789 integ-alg sha1-96 |
| 33 | // use-anti-replay udp-encap |
| 34 | // # protect IP-IP with IPsec |
| 35 | // ipsec tunnel protect ipip0 sa-in 30 sa-out 20 |
| 36 | // # subnet to route through IPsec (clear -> cipher) |
| 37 | // ip route add table 1 192.168.0.0/24 via ipip0 |
| 38 | // # default route for IPsec packets after encapsulation (clear -> cipher) |
| 39 | // ip route add table 20.0.0.0/0 via 10.10.10.1 loop0 |
| 40 | // # default route for clear-text packets after decapsulation |
| 41 | // # (cipher -> clear) |
| 42 | // ip route add table 30.0.0.0/0 via 10.10.10.1 loop0 |
| 43 | // |
| 44 | #include <iostream> |
| 45 | #include <algorithm> |
| 46 | #include <vapi/vapi.hpp> |
| 47 | #include <vapi/vpe.api.vapi.hpp> |
| 48 | DEFINE_VAPI_MSG_IDS_VPE_API_JSON |
| 49 | #include <vapi/interface.api.vapi.hpp> |
| 50 | DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON |
| 51 | #include <vapi/ip.api.vapi.hpp> |
| 52 | DEFINE_VAPI_MSG_IDS_IP_API_JSON |
| 53 | #include <vapi/ipip.api.vapi.hpp> |
| 54 | DEFINE_VAPI_MSG_IDS_IPIP_API_JSON |
| 55 | #include <vapi/ipsec.api.vapi.hpp> |
| 56 | DEFINE_VAPI_MSG_IDS_IPSEC_API_JSON |
| 57 | |
| 58 | template <typename MyRequest> |
| 59 | static auto & |
| 60 | execute (vapi::Connection &con, MyRequest &req) |
| 61 | { |
| 62 | // send the command to VPP |
| 63 | auto err = req.execute (); |
| 64 | if (VAPI_OK != err) |
| 65 | throw std::runtime_error ("execute()"); |
| 66 | // active-wait for command result |
| 67 | do |
| 68 | { |
| 69 | err = con.wait_for_response (req); |
| 70 | } |
| 71 | while (VAPI_EAGAIN == err); |
| 72 | if (VAPI_OK != err) |
| 73 | throw std::runtime_error ("wait_for_response()"); |
| 74 | // verify the reply error code |
| 75 | auto &rmp = req.get_response ().get_payload (); |
| 76 | if (0 != rmp.retval) |
| 77 | throw std::runtime_error ("wrong return code"); |
| 78 | return rmp; |
| 79 | } |
| 80 | |
| 81 | static void |
| 82 | route_add (vapi::Connection &con, const int vrf, const unsigned char prefix[4], |
| 83 | const int plen, const int sw_if_index, const unsigned char nh[4]) |
| 84 | { |
| 85 | std::cout << "Adding route..." << std::endl; |
| 86 | // ip route add table <vrf> <prefix>/<plen> via <nh> <sw_if_index> |
| 87 | vapi::Ip_route_add_del route (con, |
| 88 | 1); // cf. src/vnet/ip/ip.api:ip_route_add_del |
| 89 | // - we allocate space for 1 path (nh) |
| 90 | auto &mp = route.get_request ().get_payload (); |
| 91 | mp.is_add = true; |
| 92 | mp.is_multipath = false; |
| 93 | mp.route.table_id = vrf; |
| 94 | mp.route.prefix.address.af = ADDRESS_IP4; |
| 95 | std::copy (prefix, prefix + 4, mp.route.prefix.address.un.ip4); |
| 96 | mp.route.prefix.len = plen; |
| 97 | mp.route.n_paths = |
| 98 | 1; // 1 path, must match allocation in route declaration above |
| 99 | // cf. src/vnet/fib/fib_types.api:fib_path |
| 100 | mp.route.paths[0].sw_if_index = sw_if_index; |
| 101 | mp.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; |
| 102 | std::copy (nh, nh + 4, mp.route.paths[0].nh.address.ip4); |
| 103 | execute (con, route); |
| 104 | } |
| 105 | |
| 106 | static void |
| 107 | ipsec_sa_add (vapi::Connection &con, const int id, const int spi) |
| 108 | { |
| 109 | std::cout << "Adding SA " << id << "..." << std::endl; |
| 110 | // ipsec sa add <id> spi <spi> crypto-key 01234567890123456789012345678901 |
| 111 | // crypto-alg aes-cbc-128 integ-key 01234567890123456789 integ-alg sha1-96 |
| 112 | // use-anti-replay udp-encap |
| 113 | vapi::Ipsec_sad_entry_add_del_v2 ipsec ( |
| 114 | con); // cf. src/vnet/ipsec/ipsec.api:ipsec_sad_entry_add_del_v2 |
| 115 | auto &mp = ipsec.get_request ().get_payload (); |
| 116 | mp.is_add = true; |
| 117 | // cf. src/vnet/ipsec/ipsec_types.api:ipsec_sad_entry_v2 |
| 118 | mp.entry.sad_id = id; // user-defined SA id |
| 119 | mp.entry.spi = spi; |
| 120 | mp.entry.protocol = IPSEC_API_PROTO_ESP; |
| 121 | mp.entry.crypto_algorithm = IPSEC_API_CRYPTO_ALG_AES_CBC_128; |
| 122 | const char key[] = |
| 123 | "\x01\x23\x45\x67\x89\x01\x23\x45\x67\x89\x01\x23\x45\x67\x89\x01"; |
| 124 | // cf. src/vnet/ipsec/ipsec_types.api:key |
| 125 | mp.entry.crypto_key.length = sizeof (key) - 1; |
| 126 | std::copy (key, key + sizeof (key) - 1, mp.entry.crypto_key.data); |
| 127 | mp.entry.integrity_algorithm = IPSEC_API_INTEG_ALG_SHA1_96; |
| 128 | mp.entry.integrity_key.length = sizeof (key) - 1; |
| 129 | std::copy (key, key + sizeof (key) - 1, mp.entry.integrity_key.data); |
| 130 | mp.entry.flags = (vapi_enum_ipsec_sad_flags) ( |
| 131 | IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY | IPSEC_API_SAD_FLAG_UDP_ENCAP); |
| 132 | mp.entry.udp_src_port = 4500; |
| 133 | mp.entry.udp_dst_port = 4500; |
| 134 | execute (con, ipsec); |
| 135 | } |
| 136 | |
| 137 | int |
| 138 | main () |
| 139 | { |
| 140 | // Connect to VPP: client name, API prefix, max outstanding request, response |
| 141 | // queue size |
| 142 | std::cout << "Connecting to VPP..." << std::endl; |
| 143 | vapi::Connection con; |
| 144 | auto err = con.connect ("example_client", nullptr, 32, 32); |
| 145 | if (VAPI_OK != err) |
| 146 | throw std::runtime_error ("connection to VPP failed"); |
| 147 | |
| 148 | try |
| 149 | { |
| 150 | |
| 151 | std::cout << "Configuring address..." << std::endl; |
| 152 | { |
| 153 | // set int addr <uplink> 10.10.10.10/24 |
| 154 | vapi::Sw_interface_add_del_address addr ( |
| 155 | con); // cf. src/vnet/interface.api:sw_interface_add_del_address |
| 156 | auto &mp = addr.get_request ().get_payload (); |
| 157 | mp.sw_if_index = 1; // uplink |
| 158 | mp.is_add = true; |
| 159 | mp.prefix.address.af = ADDRESS_IP4; |
| 160 | const char ip[] = { 0x0a, 0x0a, 0x0a, 0x0a }; // 10.10.10.10 |
| 161 | std::copy (ip, ip + 4, mp.prefix.address.un.ip4); |
| 162 | mp.prefix.len = 24; |
| 163 | execute (con, addr); |
| 164 | } |
| 165 | |
| 166 | std::cout << "Creating IP-IP tunnel..." << std::endl; |
| 167 | unsigned ipip_sw_if_index; |
| 168 | { |
| 169 | // create ipip tunnel src 10.10.10.1 dst 10.20.20.20 outer-table-id 2 |
| 170 | vapi::Ipip_add_tunnel ipip ( |
| 171 | con); // cf. src/vnet/ipip/ipip.api:ipip_add_tunnel |
| 172 | auto &mp = ipip.get_request ().get_payload (); |
| 173 | mp.tunnel.instance = ~0; |
| 174 | mp.tunnel.src.af = ADDRESS_IP4; |
| 175 | const char src[] = { 0x0a, 0x0a, 0x0a, 0x0a }; // 10.10.10.10 |
| 176 | std::copy (src, src + 4, mp.tunnel.src.un.ip4); |
| 177 | mp.tunnel.dst.af = ADDRESS_IP4; |
| 178 | const char dst[] = { 0x0a, 0x14, 0x14, 0x14 }; // 10.20.20.20 |
| 179 | std::copy (dst, dst + 4, mp.tunnel.dst.un.ip4); |
| 180 | mp.tunnel.table_id = |
| 181 | 2; // VRF 2 - encapsulated (ciphered) packets should be lookup'ed in |
| 182 | // VRF 2 to determine path |
| 183 | auto &rmp = execute (con, ipip); |
| 184 | ipip_sw_if_index = |
| 185 | rmp.sw_if_index; // save ipip tunnel index for later use |
| 186 | } |
| 187 | |
| 188 | std::cout << "Moving IP-IP tunnel to VRF 3..." << std::endl; |
| 189 | { |
| 190 | // set int ip table ipip0 3 |
| 191 | vapi::Sw_interface_set_table table ( |
| 192 | con); // cf. src/vnet/interface.api:sw_interface_set_table |
| 193 | auto &mp = table.get_request ().get_payload (); |
| 194 | mp.sw_if_index = ipip_sw_if_index; |
| 195 | mp.vrf_id = 3; // VRF 3 - decapsulated (deciphered) packets should be |
| 196 | // lookup'ed in VRF 3 to determine path |
| 197 | execute (con, table); |
| 198 | } |
| 199 | |
| 200 | std::cout << "Configuring IP-IP tunnel as unnumbered..." << std::endl; |
| 201 | { |
| 202 | // set int unnum ipip0 use <uplink> |
| 203 | vapi::Sw_interface_set_unnumbered unnum ( |
| 204 | con); // cf. src/vnet/interface.api:sw_interface_set_unnumbered |
| 205 | auto &mp = unnum.get_request ().get_payload (); |
| 206 | mp.sw_if_index = 1; // uplink |
| 207 | mp.unnumbered_sw_if_index = ipip_sw_if_index; |
| 208 | execute (con, unnum); |
| 209 | } |
| 210 | |
| 211 | std::cout << "Setting IP-IP tunnel up..." << std::endl; |
| 212 | { |
| 213 | // set int state ipip0 up |
| 214 | vapi::Sw_interface_set_flags flags ( |
| 215 | con); // cf. src/vnet/interface.api:sw_interface_set_flags |
| 216 | auto &mp = flags.get_request ().get_payload (); |
| 217 | mp.sw_if_index = ipip_sw_if_index; |
| 218 | mp.flags = IF_STATUS_API_FLAG_ADMIN_UP; |
| 219 | execute (con, flags); |
| 220 | } |
| 221 | |
| 222 | // ipsec sa add 20 spi 200 crypto-key 01234567890123456789012345678901 |
| 223 | // crypto-alg aes-cbc-128 integ-key 01234567890123456789 integ-alg |
| 224 | // sha1-96 use-anti-replay udp-encap |
| 225 | ipsec_sa_add (con, 20, 200); |
| 226 | |
| 227 | // ipsec sa add 30 spi 300 crypto-key 01234567890123456789012345678901 |
| 228 | // crypto-alg aes-cbc-128 integ-key 01234567890123456789 integ-alg |
| 229 | // sha1-96 use-anti-replay udp-encap |
| 230 | ipsec_sa_add (con, 30, 300); |
| 231 | |
| 232 | std::cout << "Protecting IP-IP tunnel..." << std::endl; |
| 233 | { |
| 234 | // ipsec tunnel protect ipip0 sa-in 30 sa-out 20 |
| 235 | vapi::Ipsec_tunnel_protect_update tun ( |
| 236 | con, 1); // cf. src/vnet/ipsec/ipsec.api:ipsec_tunnel_protect_update |
| 237 | // - we allocate space for 1 sa_in |
| 238 | auto &mp = tun.get_request ().get_payload (); |
| 239 | // cf. src/vnet/ipsec/ipsec.api:ipsec_tunnel_protect |
| 240 | mp.tunnel.sw_if_index = ipip_sw_if_index; |
| 241 | mp.tunnel.sa_out = 20; |
| 242 | mp.tunnel.n_sa_in = |
| 243 | 1; // 1 SA, must match allocation in declaration above |
| 244 | mp.tunnel.sa_in[0] = 30; |
| 245 | execute (con, tun); |
| 246 | } |
| 247 | |
| 248 | // add route for clear-text packets to be encrypted |
| 249 | // ip route add table 1 192.168.0.0/24 via ipip0 |
| 250 | route_add (con, |
| 251 | 1, // VRF 1 |
| 252 | (const unsigned char[]){ 192, 168, 0, 0 }, |
| 253 | 24, // 192.168.0.0/24 |
| 254 | ipip_sw_if_index, // ipip0 |
| 255 | (const unsigned char[]){}); // 0 |
| 256 | |
| 257 | // add default route for encrypted packets (clear -> ciphered) |
| 258 | // ip route add table 2 0.0.0.0/0 via 10.10.10.1 <uplink> |
| 259 | route_add (con, |
| 260 | 2, // VRF 2 |
| 261 | (const unsigned char[]){}, 0, // 0.0.0.0/0 |
| 262 | 1, // <uplink> |
| 263 | (const unsigned char[]){ 10, 10, 10, 1 }); // 10.0.0.1 |
| 264 | |
| 265 | // add default route for decrypted packets (ciphered -> clear) |
| 266 | // ip route add table 3 0.0.0.0/0 via 10.10.10.1 <uplink> |
| 267 | route_add (con, |
| 268 | 3, // VRF 3 |
| 269 | (const unsigned char[]){}, 0, // 0.0.0.0/0 |
| 270 | 1, // <uplink> |
| 271 | (const unsigned char[]){ 10, 10, 10, 1 }); // 10.0.0.1 |
| 272 | } |
| 273 | catch (...) |
| 274 | { |
| 275 | std::cerr << "Failure" << std::endl; |
| 276 | con.disconnect (); |
| 277 | return 1; |
| 278 | } |
| 279 | |
| 280 | con.disconnect (); |
| 281 | std::cerr << "Success" << std::endl; |
| 282 | return 0; |
| 283 | } |