docs: C & C++ apis examples 95/34095/2
authorNathan Skrzypczak <nathan.skrzypczak@gmail.com>
Wed, 13 Oct 2021 10:40:42 +0000 (12:40 +0200)
committerDave Wallace <dwallacelf@gmail.com>
Wed, 13 Oct 2021 23:22:32 +0000 (23:22 +0000)
Type: docs

Change-Id: I5b6c388332bdd3a29777d728c3357816c8411ea2
Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
docs/_scripts/Makefile
docs/index.rst
docs/interfacing/c/index.rst [new file with mode: 0644]
docs/interfacing/cpp/api_example/Makefile [new file with mode: 0644]
docs/interfacing/cpp/api_example/api_example.cc [new file with mode: 0644]
docs/interfacing/cpp/index.rst [new file with mode: 0644]

index dbd88bf..f9cb535 100644 (file)
@@ -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 \
index fcb63ee..3a03eb0 100644 (file)
@@ -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 (file)
index 0000000..e1de10f
--- /dev/null
@@ -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 (file)
index 0000000..e9f44d3
--- /dev/null
@@ -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 (file)
index 0000000..cc5a184
--- /dev/null
@@ -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 <iostream>
+#include <algorithm>
+#include <vapi/vapi.hpp>
+#include <vapi/vpe.api.vapi.hpp>
+DEFINE_VAPI_MSG_IDS_VPE_API_JSON
+#include <vapi/interface.api.vapi.hpp>
+DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON
+#include <vapi/ip.api.vapi.hpp>
+DEFINE_VAPI_MSG_IDS_IP_API_JSON
+#include <vapi/ipip.api.vapi.hpp>
+DEFINE_VAPI_MSG_IDS_IPIP_API_JSON
+#include <vapi/ipsec.api.vapi.hpp>
+DEFINE_VAPI_MSG_IDS_IPSEC_API_JSON
+
+template <typename MyRequest>
+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 <vrf> <prefix>/<plen> via <nh> <sw_if_index>
+  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 <id> spi <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 <uplink> 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 <uplink>
+       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 <uplink>
+      route_add (con,
+                2,                                         // VRF 2
+                (const unsigned char[]){}, 0,              // 0.0.0.0/0
+                1,                                         // <uplink>
+                (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 <uplink>
+      route_add (con,
+                3,                                         // VRF 3
+                (const unsigned char[]){}, 0,              // 0.0.0.0/0
+                1,                                         // <uplink>
+                (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 (file)
index 0000000..8351e9c
--- /dev/null
@@ -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
+