docs: add container functional test writeup
[vpp.git] / docs / usecases / container_test.md
1 Container-based network simulation
2 ==================================
3
4 The "make test" framework provides a good way to test individual
5 features. However, when testing several features at once - or
6 validating nontrivial configurations - it may prove difficult or
7 impossible to use the unit-test framework.
8
9 This note explains how to set up lxc/lxd, and a 5-container testbed to
10 test a split-tunnel nat + ikev2 + ipsec + ipv6 prefix-delegation
11 scenario.
12
13 OS / Distro test results
14 ========================
15
16 This setup has been tested on an Ubuntu 18.04 LTS system. If you're
17 feeling adventurous, the same scenario also worked on a recent Ubuntu
18 20.04 "preview" daily build.
19
20 Other distros may work fine, or not at all.
21
22 Proxy Server
23 ============
24
25 If you need to use a proxy server e.g. from a lab system, you'll
26 probably need to set HTTP_PROXY, HTTPS_PROXY, http_proxy and
27 https_proxy in /etc/environment. Directly setting variables in the
28 environment doesn't work. The lxd snap _daemon_ needs the proxy settings,
29 not the user interface.
30
31 Something like so:
32
33 ```
34     HTTP_PROXY=http://my.proxy.server:8080
35     HTTPS_PROXY=http://my.proxy.server:4333
36     http_proxy=http://my.proxy.server:8080
37     https_proxy=http://my.proxy.server:4333
38 ```
39
40 Install and configure lxd
41 =========================
42
43 Install the lxd snap. The lxd snap is up to date, as opposed to the
44 results of "sudo apt-get install lxd".
45
46 ```
47     # snap install lxd
48     # lxd init
49 ```
50
51 "lxd init" asks several questions. With the exception of the storage
52 pool, take the defaults. To match the configs shown below, create a
53 storage pool named "vpp." Storage pools of type "zfs" and "files" have
54 been tested successfully.
55
56 zfs is more space-efficient. "lxc copy" is infinitely faster with
57 zfs. The path for the zfs storage pool is under /var. Do not replace
58 it with a symbolic link, unless you want to rebuild all of your
59 containers from scratch. Ask me how I know that.
60
61 Create three network segments
62 =============================
63
64 Aka, linux bridges.
65
66 ```
67     # lxc network create dora
68     # lxc network create internet
69     # lxc network create swan
70 ```
71
72 We'll explain the test topology in a bit. Stay tuned.
73
74 Set up the default container profile
75 ====================================
76
77 Execute "lxc profile edit default", and install the following
78 configuration. Note that the "shared" directory should mount your vpp
79 workspaces. With that trick, you can edit code from any of the
80 containers, run vpp without installing it, etc.
81
82 ```
83     config: {}
84     description: Default LXD profile
85     devices:
86       eth0:
87         name: eth0
88         network: lxdbr0
89         type: nic
90       eth1:
91         name: eth1
92         nictype: bridged
93         parent: internet
94         type: nic
95       eth2:
96         name: eth2
97         nictype: bridged
98         parent: dora
99         type: nic
100       eth3:
101         name: eth3
102         nictype: bridged
103         parent: swan
104         type: nic
105       root:
106         path: /
107         pool: vpp
108         type: disk
109       shared:
110         path: /scratch
111         source: /scratch
112         type: disk
113     name: default
114 ```
115
116 Set up the network configurations
117 =================================
118
119 Edit the fake "internet" backbone:
120
121 ```
122   # lxc network edit internet
123 ```
124
125 Install the ip addresses shown below, to avoid having to rebuild the vpp
126 and host configuration:
127
128 ```
129     config:
130       ipv4.address: 10.26.68.1/24
131       ipv4.dhcp.ranges: 10.26.68.10-10.26.68.50
132       ipv4.nat: "true"
133       ipv6.address: none
134       ipv6.nat: "false"
135     description: ""
136     name: internet
137     type: bridge
138     used_by:
139     managed: true
140     status: Created
141     locations:
142     - none
143 ```
144
145 Repeat the process with the "dora" and "swan" networks, using these
146 configurations:
147
148 ### dora network configuration
149
150 ```
151     config:
152       ipv4.address: 10.166.14.1/24
153       ipv4.dhcp.ranges: 10.166.14.10-10.166.14.50
154       ipv4.nat: "true"
155       ipv6.address: none
156       ipv6.nat: "false"
157     description: ""
158     name: dora
159     type: bridge
160     used_by:
161     managed: true
162     status: Created
163     locations:
164     - none
165 ```
166 ### swan network configuration
167
168 ```
169     config:
170       ipv4.address: 10.219.188.1/24
171       ipv4.dhcp.ranges: 10.219.188.10-10.219.188.50
172       ipv4.nat: "true"
173       ipv6.address: none
174       ipv6.nat: "false"
175     description: ""
176     name: swan
177     type: bridge
178     used_by:
179     managed: true
180     status: Created
181     locations:
182     - none
183 ```
184
185 Create a "master" container image
186 =================================
187
188 The master container image should be set up so that you can
189 build vpp, ssh into the container, edit source code, run gdb, etc.
190
191 Make sure that e.g. public key auth ssh works.
192
193 ```
194     # lxd launch ubuntu:18.04 dora
195     <spew>
196     # lxc exec dora bash
197     dora# cd /scratch/my-vpp-workspace
198     dora# apt-get install make ssh
199     dora# make install-dep
200     dora# exit
201     # lxc stop dora
202 ```
203
204 Mark the container image privileged. If you forget this step, you'll
205 trip over a netlink error (-11) aka EAGAIN when you try to roll in the
206 vpp configurations.
207
208 ```
209     # lxc config set dora security.privileged "true"
210 ```
211
212 Duplicate the "master" container image
213 ======================================
214
215 To avoid having to configure N containers, be sure that the master
216 container image is fully set up before you help it have children:
217
218 ```
219     # lxc copy dora dorahost
220     # lxc copy dora swan
221     # lxc copy dora swanhost
222     # lxc copy dora dhcpserver    # optional, to test ipv6 prefix delegation
223 ```
224
225 Install handy script
226 ====================
227
228 See below for a handly script which executes lxc commands across the
229 current set of running containers. I call it "lxc-foreach," feel free
230 to call the script Ishmael if you like.
231
232 Examples:
233
234 ```
235     $ lxc-foreach start
236     <issues "lxc start" for each container in the list>
237 ```
238
239 After a few seconds, use this one to open an ssh connection to each
240 container. The ssh command parses the output of "lxc info," which
241 displays container ip addresses.
242
243 ```
244     $ lxc-foreach ssh
245 ```
246
247 Here's the script:
248
249 ```
250     #!/bin/bash
251
252     set -u
253     export containers="dora dorahost swan swanhost dhcpserver"
254
255     if [ x$1 = "x" ] ; then
256         echo missing command
257         exit 1
258     fi
259
260     if [ $1 = "ssh" ] ; then
261         for c in $containers
262         do
263             inet=`lxc info $c | grep eth0 | grep -v inet6 | head -1 | cut -f 3`
264             if [ x$inet = "x" ] ; then
265                 echo $c not started
266             else
267                 gnome-terminal --command "/usr/bin/ssh $inet"
268             fi
269         done
270     exit 0
271     fi
272
273     for c in $containers
274     do
275         echo lxc $1 $c
276         lxc $1 $c
277     done
278
279     exit 0
280 ```
281
282 Test topology
283 =============
284
285 Finally, we're ready to describe a test topology. First, a picture:
286
287 ```
288     ===+======== management lan/bridge lxdbr0 (dhcp) ===========+===
289        |                             |                          |
290        |                             |                          |
291        |                             |                          |
292        v                             |                          v
293       eth0                           |                         eth0
294     +------+ eth1                                       eth1 +------+
295     | dora | 10.26.88.100 <= internet bridge => 10.26.88.101 | swan |
296     +------+                                                 +------+
297       eth2 / bvi0 10.166.14.2        |       10.219.188.2 eth3 / bvi0
298        |                             |                          |
299        | ("dora" bridge)             |          ("swan" bridge) |
300        |                             |                          |
301        v                             |                          v
302       eth2 10.166.14.3               |           eth3 10.219.188.3
303     +----------+                     |                   +----------+
304     | dorahost |                     |                   | dorahost |
305     +----------+                     |                   +----------+
306       eth0 (management lan) <========+========> eth0 (management lan)
307 ```
308
309 ### Test topology discussion
310
311 This topology is suitable for testing almost any tunnel encap/decap
312 scenario.  The two containers "dorahost" and "swanhost" are end-stations
313 connected to two vpp instances running on "dora" and "swan".
314
315 We leverage the Linux end-station network stacks to generate traffic
316 of all sorts.
317
318 The so-called "internet" bridge models the public internet. The "dora" and
319 "swan" bridges connect vpp instances to local hosts
320
321 End station configs
322 ===================
323
324 The end-station Linux configurations set up the eth2 and eth3 ip
325 addresses shown above, and add tunnel routes to the opposite
326 end-station networks.
327
328 ### dorahost configuration
329
330 ```
331     ifconfig eth2 10.166.14.3/24 up
332     route add -net 10.219.188.0/24 gw 10.166.14.2
333 ```
334
335 ### swanhost configuration
336
337 ```
338     sudo ifconfig eth3 10.219.188.3/24 up
339     sudo route add -net 10.166.14.0/24 gw 10.219.188.2
340 ```
341
342 VPP configs
343 ===========
344
345 Split nat44 / ikev2 + ipsec tunneling, with ipv6 prefix delegation in
346 the "dora" config.
347
348 ### dora configuration
349
350 ```
351     set term pag off
352
353     comment { "internet" }
354     create host-interface name eth1
355     set int ip address host-eth1 10.26.68.100/24
356     set int ip6 table host-eth1 0
357     set int state host-eth1 up
358
359     comment { default route via swan }
360     ip route add 0.0.0.0/0 via 10.26.68.101
361
362     comment { "dora-private-net" }
363     create host-interface name eth2
364     bvi create instance 0
365     set int l2 bridge bvi0 1 bvi
366     set int ip address bvi0 10.166.14.2/24
367     set int state bvi0 up
368     set int l2 bridge host-eth2 1
369     set int state host-eth2 up
370
371
372     nat44 add interface address host-eth1
373     set interface nat44 in host-eth2 out host-eth1
374     nat44 add identity mapping external host-eth1 udp 500
375     nat44 add identity mapping external host-eth1 udp 4500
376     comment { nat44 untranslated subnet 10.219.188.0/24 }
377
378     comment { responder profile }
379     ikev2 profile add swan
380     ikev2 profile set swan udp-encap
381     ikev2 profile set swan auth rsa-sig cert-file /scratch/setups/doracert.pem
382     set ikev2 local key /scratch/setups/swankey.pem
383     ikev2 profile set swan id local fqdn swan.barachs.net
384     ikev2 profile set swan id remote fqdn broiler2.barachs.net
385     ikev2 profile set swan traffic-selector remote ip-range 10.219.188.0 - 10.219.188.255 port-range 0 - 65535 protocol 0
386     ikev2 profile set swan traffic-selector local ip-range 10.166.14.0 - 10.166.14.255 port-range 0 - 65535 protocol 0
387     create ipip tunnel src 10.26.68.100 dst 10.26.68.101
388     ikev2 profile set swan tunnel ipip0
389
390     comment { ipv6 prefix delegation }
391     ip6 nd address autoconfig host-eth1 default-route
392     dhcp6 client host-eth1
393     dhcp6 pd client host-eth1 prefix group hgw
394     set ip6 address bvi0 prefix group hgw ::2/56
395     ip6 nd address autoconfig bvi0 default-route
396     ip6 nd bvi0 ra-interval 5 3 ra-lifetime 180
397
398     set int mtu packet 1390 ipip0
399     set int unnum ipip0 use host-eth1
400     ip route add 10.219.188.0/24 via ipip0
401 ```
402
403 ### swan configuration
404
405 ```
406     set term pag off
407
408     comment { "internet" }
409     create host-interface name eth1
410     comment { set dhcp client intfc host-eth1 hostname swan }
411     set int ip address host-eth1 10.26.68.101/24
412     set int state host-eth1 up
413
414     comment { default route via "internet gateway" }
415     comment { ip route add 0.0.0.0/0 via 10.26.68.1 }
416
417     comment { "swan-private-net" }
418     create host-interface name eth3
419     bvi create instance 0
420     set int l2 bridge bvi0 1 bvi
421     set int ip address bvi0 10.219.188.2/24
422     set int state bvi0 up
423     set int l2 bridge host-eth3 1
424     set int state host-eth3 up
425
426     nat44 add interface address host-eth1
427     set interface nat44 in bvi0 out host-eth1
428     nat44 add identity mapping external host-eth1 udp 500
429     nat44 add identity mapping external host-eth1 udp 4500
430     comment { nat44 untranslated subnet 10.166.14.0/24 }
431
432     comment { initiator profile }
433     ikev2 profile add dora
434     ikev2 profile set dora udp-encap
435     ikev2 profile set dora auth rsa-sig cert-file /scratch/setups/swancert.pem
436     set ikev2 local key /scratch/setups/dorakey.pem
437     ikev2 profile set dora id local fqdn broiler2.barachs.net
438     ikev2 profile set dora id remote fqdn swan.barachs.net
439
440     ikev2 profile set dora traffic-selector remote ip-range 10.166.14.0 - 10.166.14.255 port-range 0 - 65535 protocol 0
441     ikev2 profile set dora traffic-selector local ip-range 10.219.188.0 - 10.219.188.255 port-range 0 - 65535 protocol 0
442
443     ikev2 profile set dora responder host-eth1 10.26.68.100
444     ikev2 profile set dora ike-crypto-alg aes-cbc 256  ike-integ-alg sha1-96  ike-dh modp-2048
445     ikev2 profile set dora esp-crypto-alg aes-cbc 256  esp-integ-alg sha1-96  esp-dh ecp-256
446     ikev2 profile set dora sa-lifetime 3600 10 5 0
447
448     create ipip tunnel src 10.26.68.101 dst 10.26.68.100
449     ikev2 profile set dora tunnel ipip0
450     ikev2 initiate sa-init dora
451
452     set int mtu packet 1390 ipip0
453     set int unnum ipip0 use host-eth1
454     ip route add 10.166.14.0/24 via ipip0
455 ```
456
457 IKEv2 certificate setup
458 =======================
459
460 In both of the vpp configurations, you'll see "/scratch/setups/xxx.pem"
461 mentioned. These certificates are used in the ikev2 key exchange.
462
463 Here's how to generate the certificates:
464
465 ```
466     openssl req -x509 -nodes -newkey rsa:4096 -keyout dorakey.pem -out doracert.pem -days 3560
467     openssl x509 -text -noout -in doracert.pem
468     openssl req -x509 -nodes -newkey rsa:4096 -keyout swankey.pem -out swancert.pem -days 3560
469     openssl x509 -text -noout -in swancert.pem
470 ```
471
472 Make sure that the "dora" and "swan" configurations point to the certificates.
473
474 DHCPv6 server setup
475 ===================
476
477 If you need an ipv6 dhcp server to test ipv6 prefix delegation,
478 create the "dhcpserver" container as shown above.
479
480 Install the "isc-dhcp-server" Debian package:
481
482 ```
483     sudo apt-get install isc-dhcp-server
484 ```
485
486 ### /etc/dhcp/dhcpd6.conf
487
488 Edit the dhcpv6 configuration and add an ipv6 subnet with prefix
489 delegation. For example:
490
491 ```
492     subnet6 2001:db01:0:1::/64 {
493             range6 2001:db01:0:1::1 2001:db01:0:1::9;
494             prefix6 2001:db01:0:100:: 2001:db01:0:200::/56;
495     }
496 ```
497
498 Add an ipv6 address on eth1, which is connected to the "internet"
499 bridge, and start the dhcp server. I use the following trivial bash
500 script, which runs the dhcp6 server in the foreground and produces
501 dhcp traffic spew:
502
503 ```
504     #!/bin/bash
505     ifconfig eth1 inet6 add 2001:db01:0:1::10/64 || true
506     dhcpd -6 -d -cf /etc/dhcp/dhcpd6.conf
507 ```
508
509 The "|| true" bit keeps going if eth1 already has the indicated ipv6
510 address.