From cbb4e460c481223282774b37add0bf9fb9e6170a Mon Sep 17 00:00:00 2001 From: Nathan Skrzypczak Date: Wed, 13 Oct 2021 12:40:42 +0200 Subject: [PATCH] docs: C & C++ apis examples Type: docs Change-Id: I5b6c388332bdd3a29777d728c3357816c8411ea2 Signed-off-by: Nathan Skrzypczak --- docs/_scripts/Makefile | 9 + docs/index.rst | 2 + docs/interfacing/c/index.rst | 10 + docs/interfacing/cpp/api_example/Makefile | 16 ++ docs/interfacing/cpp/api_example/api_example.cc | 283 ++++++++++++++++++++++++ docs/interfacing/cpp/index.rst | 26 +++ 6 files changed, 346 insertions(+) create mode 100644 docs/interfacing/c/index.rst create mode 100644 docs/interfacing/cpp/api_example/Makefile create mode 100644 docs/interfacing/cpp/api_example/api_example.cc create mode 100644 docs/interfacing/cpp/index.rst diff --git a/docs/_scripts/Makefile b/docs/_scripts/Makefile index dbd88bf9a20..f9cb535182f 100644 --- a/docs/_scripts/Makefile +++ b/docs/_scripts/Makefile @@ -139,6 +139,15 @@ includes-render: template-index: @sed -ie "s/__VPP_VERSION__/${VPP_VERSION}/g" ${DOCS_DIR}/index.rst @sed -ie "s/__BUILT_ON__/${BUILT_ON}/g" ${DOCS_DIR}/index.rst + @( \ + for f in $$(grep -l -R __REPOSITORY_URL__ ${DOCS_DIR} | grep -e '\.rst$$' -e '\.md$$' ) ;\ + do \ + if [ ! -z $${f} ]; then \ + echo "TEMPLATING $${f}" ;\ + sed -ie "s@__REPOSITORY_URL__@${REPOSITORY_URL}@g" $${f} ;\ + fi ;\ + done ; \ + ) .NOTPARALLEL: $(SIPHON_FILES) $(SIPHON_FILES): $(SCRIPTS_DIR)/siphon-generate \ diff --git a/docs/index.rst b/docs/index.rst index fcb63eea11a..3a03eb0f34a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -90,6 +90,8 @@ For more details click on the links below or press next. :maxdepth: 2 interfacing/binapi/index + interfacing/c/index + interfacing/cpp/index interfacing/go/index interfacing/rust/index interfacing/libmemif/index diff --git a/docs/interfacing/c/index.rst b/docs/interfacing/c/index.rst new file mode 100644 index 00000000000..e1de10ff77c --- /dev/null +++ b/docs/interfacing/c/index.rst @@ -0,0 +1,10 @@ +.. _cvpp: + +============ +C api client +============ + +You can find a good example on how to build a binary API client in C here : + +`src/vpp-api/vapi/vapi_c_test.c <__REPOSITORY_URL__/src/vpp-api/vapi/vapi_c_test.c>`_ + diff --git a/docs/interfacing/cpp/api_example/Makefile b/docs/interfacing/cpp/api_example/Makefile new file mode 100644 index 00000000000..e9f44d3397d --- /dev/null +++ b/docs/interfacing/cpp/api_example/Makefile @@ -0,0 +1,16 @@ +CXXFLAGS:=-std=c++14 -Wextra -Wall -g -O2 +CXXFLAGS+= -Wno-unused-parameter +#CXXFLAGS+= -O0 +LDFLAGS:=-g +LDLIBS:=-lvapiclient +# Customize the lines below if VPP headers and libraries are in non-standard paths +#CXXFLAGS+= -I/your/vpp/build-root/install-vpp_debug-native/vpp/include +#LDFLAGS+= -Wl,-rpath,/your/vpp/build-root/install-vpp_debug-native/vpp/lib +#LDFLAGS+= -L/your/vpp/build-root/install-vpp_debug-native/vpp/lib + +all: api_example + +clean: + $(RM) api_example + +.PHONY: all clean diff --git a/docs/interfacing/cpp/api_example/api_example.cc b/docs/interfacing/cpp/api_example/api_example.cc new file mode 100644 index 00000000000..cc5a1848e2b --- /dev/null +++ b/docs/interfacing/cpp/api_example/api_example.cc @@ -0,0 +1,283 @@ +// +// 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; +} diff --git a/docs/interfacing/cpp/index.rst b/docs/interfacing/cpp/index.rst new file mode 100644 index 00000000000..8351e9c504a --- /dev/null +++ b/docs/interfacing/cpp/index.rst @@ -0,0 +1,26 @@ +.. _cppvpp: + +============== +C++ api client +============== + +This describes how to write a C++ api client connecting to VPP's binary API. + +Connecting to VPP is done with : + +:: + + auto err = con.connect("example_client", nullptr, 32, 32); + + +You can specify the path to the api socket/shared memory you want to connect to +with the second parameter (set to ``nullptr``, meaning default) + +.. literalinclude:: ./api_example/api_example.cc + :language: cpp + +To build this you could use the following makefile + +.. literalinclude:: ./api_example/Makefile + :language: makefile + -- 2.16.6