blob: cc5a1848e2b1f018ae9c2e4b409292cea3304c39 [file] [log] [blame]
Nathan Skrzypczakcbb4e462021-10-13 12:40:42 +02001//
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>
48DEFINE_VAPI_MSG_IDS_VPE_API_JSON
49#include <vapi/interface.api.vapi.hpp>
50DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON
51#include <vapi/ip.api.vapi.hpp>
52DEFINE_VAPI_MSG_IDS_IP_API_JSON
53#include <vapi/ipip.api.vapi.hpp>
54DEFINE_VAPI_MSG_IDS_IPIP_API_JSON
55#include <vapi/ipsec.api.vapi.hpp>
56DEFINE_VAPI_MSG_IDS_IPSEC_API_JSON
57
58template <typename MyRequest>
59static auto &
60execute (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
81static void
82route_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
106static void
107ipsec_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
137int
138main ()
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}