// // VPP C++ API example // // The uplink is the 1st interface (sw_if_index is 1) and is set // to 10.10.10.10/24 The uplink gateway is 10.10.10.1 The IPsec tunnel is set // between 10.10.10.10 and 10.20.20.20 The protected subnet is 192.168.0.0/24 // VRF 1 is for uplink ingress // VRF 2 is IPsec egress (clear -> cipher) // VRF 3 is IPsec ingress (cipher -> clear) // // The following examples must be run with VPP in the following state: // ip table add 1 # VRF 1 // ip table add 2 # VRF 2 // ip table add 3 # VRF 3 // loop create # loop0 is used as uplink with sw_if_index=1 // set int ip table loop0 1 # VRF 1 is ingress // set int state loop0 up // // Then the API will configure VPP similar to this: // # configure uplink address // set int addr loop0 10.10.10.10/24 // # create the IP-IP tunnel // create ipip tunnel src 10.10.10.1 dst 10.20.20.20 outer-table-id 2 // # use VRF-3 as IPsec ingress VRF (cipher -> clear) // set int ip table ipip0 3 // set int unnum ipip0 use loop0 // set int state ipip0 up // ipsec sa add 20 spi 200 crypto-key 01234567890123456789012345678901 // crypto-alg aes-cbc-128 integ-key 01234567890123456789 integ-alg sha1-96 // use-anti-replay udp-encap // ipsec sa add 30 spi 300 crypto-key 01234567890123456789012345678901 // crypto-alg aes-cbc-128 integ-key 01234567890123456789 integ-alg sha1-96 // use-anti-replay udp-encap // # protect IP-IP with IPsec // ipsec tunnel protect ipip0 sa-in 30 sa-out 20 // # subnet to route through IPsec (clear -> cipher) // ip route add table 1 192.168.0.0/24 via ipip0 // # default route for IPsec packets after encapsulation (clear -> cipher) // ip route add table 20.0.0.0/0 via 10.10.10.1 loop0 // # default route for clear-text packets after decapsulation // # (cipher -> clear) // ip route add table 30.0.0.0/0 via 10.10.10.1 loop0 // #include #include #include #include DEFINE_VAPI_MSG_IDS_VPE_API_JSON #include DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON #include DEFINE_VAPI_MSG_IDS_IP_API_JSON #include DEFINE_VAPI_MSG_IDS_IPIP_API_JSON #include DEFINE_VAPI_MSG_IDS_IPSEC_API_JSON template static auto & execute (vapi::Connection &con, MyRequest &req) { // send the command to VPP auto err = req.execute (); if (VAPI_OK != err) throw std::runtime_error ("execute()"); // active-wait for command result do { err = con.wait_for_response (req); } while (VAPI_EAGAIN == err); if (VAPI_OK != err) throw std::runtime_error ("wait_for_response()"); // verify the reply error code auto &rmp = req.get_response ().get_payload (); if (0 != rmp.retval) throw std::runtime_error ("wrong return code"); return rmp; } static void route_add (vapi::Connection &con, const int vrf, const unsigned char prefix[4], const int plen, const int sw_if_index, const unsigned char nh[4]) { std::cout << "Adding route..." << std::endl; // ip route add table / via vapi::Ip_route_add_del route (con, 1); // cf. src/vnet/ip/ip.api:ip_route_add_del // - we allocate space for 1 path (nh) auto &mp = route.get_request ().get_payload (); mp.is_add = true; mp.is_multipath = false; mp.route.table_id = vrf; mp.route.prefix.address.af = ADDRESS_IP4; std::copy (prefix, prefix + 4, mp.route.prefix.address.un.ip4); mp.route.prefix.len = plen; mp.route.n_paths = 1; // 1 path, must match allocation in route declaration above // cf. src/vnet/fib/fib_types.api:fib_path mp.route.paths[0].sw_if_index = sw_if_index; mp.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; std::copy (nh, nh + 4, mp.route.paths[0].nh.address.ip4); execute (con, route); } static void ipsec_sa_add (vapi::Connection &con, const int id, const int spi) { std::cout << "Adding SA " << id << "..." << std::endl; // ipsec sa add spi crypto-key 01234567890123456789012345678901 // crypto-alg aes-cbc-128 integ-key 01234567890123456789 integ-alg sha1-96 // use-anti-replay udp-encap vapi::Ipsec_sad_entry_add_del_v2 ipsec ( con); // cf. src/vnet/ipsec/ipsec.api:ipsec_sad_entry_add_del_v2 auto &mp = ipsec.get_request ().get_payload (); mp.is_add = true; // cf. src/vnet/ipsec/ipsec_types.api:ipsec_sad_entry_v2 mp.entry.sad_id = id; // user-defined SA id mp.entry.spi = spi; mp.entry.protocol = IPSEC_API_PROTO_ESP; mp.entry.crypto_algorithm = IPSEC_API_CRYPTO_ALG_AES_CBC_128; const char key[] = "\x01\x23\x45\x67\x89\x01\x23\x45\x67\x89\x01\x23\x45\x67\x89\x01"; // cf. src/vnet/ipsec/ipsec_types.api:key mp.entry.crypto_key.length = sizeof (key) - 1; std::copy (key, key + sizeof (key) - 1, mp.entry.crypto_key.data); mp.entry.integrity_algorithm = IPSEC_API_INTEG_ALG_SHA1_96; mp.entry.integrity_key.length = sizeof (key) - 1; std::copy (key, key + sizeof (key) - 1, mp.entry.integrity_key.data); mp.entry.flags = (vapi_enum_ipsec_sad_flags) ( IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY | IPSEC_API_SAD_FLAG_UDP_ENCAP); mp.entry.udp_src_port = 4500; mp.entry.udp_dst_port = 4500; execute (con, ipsec); } int main () { // Connect to VPP: client name, API prefix, max outstanding request, response // queue size std::cout << "Connecting to VPP..." << std::endl; vapi::Connection con; auto err = con.connect ("example_client", nullptr, 32, 32); if (VAPI_OK != err) throw std::runtime_error ("connection to VPP failed"); try { std::cout << "Configuring address..." << std::endl; { // set int addr 10.10.10.10/24 vapi::Sw_interface_add_del_address addr ( con); // cf. src/vnet/interface.api:sw_interface_add_del_address auto &mp = addr.get_request ().get_payload (); mp.sw_if_index = 1; // uplink mp.is_add = true; mp.prefix.address.af = ADDRESS_IP4; const char ip[] = { 0x0a, 0x0a, 0x0a, 0x0a }; // 10.10.10.10 std::copy (ip, ip + 4, mp.prefix.address.un.ip4); mp.prefix.len = 24; execute (con, addr); } std::cout << "Creating IP-IP tunnel..." << std::endl; unsigned ipip_sw_if_index; { // create ipip tunnel src 10.10.10.1 dst 10.20.20.20 outer-table-id 2 vapi::Ipip_add_tunnel ipip ( con); // cf. src/vnet/ipip/ipip.api:ipip_add_tunnel auto &mp = ipip.get_request ().get_payload (); mp.tunnel.instance = ~0; mp.tunnel.src.af = ADDRESS_IP4; const char src[] = { 0x0a, 0x0a, 0x0a, 0x0a }; // 10.10.10.10 std::copy (src, src + 4, mp.tunnel.src.un.ip4); mp.tunnel.dst.af = ADDRESS_IP4; const char dst[] = { 0x0a, 0x14, 0x14, 0x14 }; // 10.20.20.20 std::copy (dst, dst + 4, mp.tunnel.dst.un.ip4); mp.tunnel.table_id = 2; // VRF 2 - encapsulated (ciphered) packets should be lookup'ed in // VRF 2 to determine path auto &rmp = execute (con, ipip); ipip_sw_if_index = rmp.sw_if_index; // save ipip tunnel index for later use } std::cout << "Moving IP-IP tunnel to VRF 3..." << std::endl; { // set int ip table ipip0 3 vapi::Sw_interface_set_table table ( con); // cf. src/vnet/interface.api:sw_interface_set_table auto &mp = table.get_request ().get_payload (); mp.sw_if_index = ipip_sw_if_index; mp.vrf_id = 3; // VRF 3 - decapsulated (deciphered) packets should be // lookup'ed in VRF 3 to determine path execute (con, table); } std::cout << "Configuring IP-IP tunnel as unnumbered..." << std::endl; { // set int unnum ipip0 use vapi::Sw_interface_set_unnumbered unnum ( con); // cf. src/vnet/interface.api:sw_interface_set_unnumbered auto &mp = unnum.get_request ().get_payload (); mp.sw_if_index = 1; // uplink mp.unnumbered_sw_if_index = ipip_sw_if_index; execute (con, unnum); } std::cout << "Setting IP-IP tunnel up..." << std::endl; { // set int state ipip0 up vapi::Sw_interface_set_flags flags ( con); // cf. src/vnet/interface.api:sw_interface_set_flags auto &mp = flags.get_request ().get_payload (); mp.sw_if_index = ipip_sw_if_index; mp.flags = IF_STATUS_API_FLAG_ADMIN_UP; execute (con, flags); } // ipsec sa add 20 spi 200 crypto-key 01234567890123456789012345678901 // crypto-alg aes-cbc-128 integ-key 01234567890123456789 integ-alg // sha1-96 use-anti-replay udp-encap ipsec_sa_add (con, 20, 200); // ipsec sa add 30 spi 300 crypto-key 01234567890123456789012345678901 // crypto-alg aes-cbc-128 integ-key 01234567890123456789 integ-alg // sha1-96 use-anti-replay udp-encap ipsec_sa_add (con, 30, 300); std::cout << "Protecting IP-IP tunnel..." << std::endl; { // ipsec tunnel protect ipip0 sa-in 30 sa-out 20 vapi::Ipsec_tunnel_protect_update tun ( con, 1); // cf. src/vnet/ipsec/ipsec.api:ipsec_tunnel_protect_update // - we allocate space for 1 sa_in auto &mp = tun.get_request ().get_payload (); // cf. src/vnet/ipsec/ipsec.api:ipsec_tunnel_protect mp.tunnel.sw_if_index = ipip_sw_if_index; mp.tunnel.sa_out = 20; mp.tunnel.n_sa_in = 1; // 1 SA, must match allocation in declaration above mp.tunnel.sa_in[0] = 30; execute (con, tun); } // add route for clear-text packets to be encrypted // ip route add table 1 192.168.0.0/24 via ipip0 route_add (con, 1, // VRF 1 (const unsigned char[]){ 192, 168, 0, 0 }, 24, // 192.168.0.0/24 ipip_sw_if_index, // ipip0 (const unsigned char[]){}); // 0 // add default route for encrypted packets (clear -> ciphered) // ip route add table 2 0.0.0.0/0 via 10.10.10.1 route_add (con, 2, // VRF 2 (const unsigned char[]){}, 0, // 0.0.0.0/0 1, // (const unsigned char[]){ 10, 10, 10, 1 }); // 10.0.0.1 // add default route for decrypted packets (ciphered -> clear) // ip route add table 3 0.0.0.0/0 via 10.10.10.1 route_add (con, 3, // VRF 3 (const unsigned char[]){}, 0, // 0.0.0.0/0 1, // (const unsigned char[]){ 10, 10, 10, 1 }); // 10.0.0.1 } catch (...) { std::cerr << "Failure" << std::endl; con.disconnect (); return 1; } con.disconnect (); std::cerr << "Success" << std::endl; return 0; }