aboutsummaryrefslogtreecommitdiffstats
path: root/docs/interfacing/cpp/api_example/api_example.cc
blob: cc5a1848e2b1f018ae9c2e4b409292cea3304c39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
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;
}