tunnel: Common types for IP tunnels
[vpp.git] / test / test_ipip.py
1 #!/usr/bin/env python3
2 """IP{4,6} over IP{v,6} tunnel functional tests"""
3
4 import unittest
5 from scapy.layers.inet6 import IPv6, Ether, IP, UDP, IPv6ExtHdrFragment, Raw
6 from scapy.all import fragment, fragment6, RandShort, defragment6
7 from framework import VppTestCase, VppTestRunner
8 from vpp_ip import DpoProto
9 from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, FibPathProto
10 from vpp_ipip_tun_interface import VppIpIpTunInterface
11 from vpp_papi import VppEnum
12 from socket import AF_INET, AF_INET6, inet_pton
13 from util import reassemble4
14
15 """ Testipip is a subclass of  VPPTestCase classes.
16
17 IPIP tests.
18
19 """
20
21
22 def ipip_add_tunnel(test, src, dst, table_id=0, dscp=0x0,
23                     flags=0):
24     """ Add a IPIP tunnel """
25     return test.vapi.ipip_add_tunnel(
26         tunnel={
27             'src': src,
28             'dst': dst,
29             'table_id': table_id,
30             'instance': 0xffffffff,
31             'dscp': dscp,
32             'flags': flags
33         }
34     )
35
36 # the number of packets to send when injecting traffic.
37 # a multiple of 8 minus one, so we test all by 8/4/2/1 loops
38 N_PACKETS = 64 - 1
39
40
41 class TestIPIP(VppTestCase):
42     """ IPIP Test Case """
43
44     @classmethod
45     def setUpClass(cls):
46         super(TestIPIP, cls).setUpClass()
47         cls.create_pg_interfaces(range(2))
48         cls.interfaces = list(cls.pg_interfaces)
49
50     @classmethod
51     def tearDownClass(cls):
52         super(TestIPIP, cls).tearDownClass()
53
54     def setUp(self):
55         super(TestIPIP, self).setUp()
56         for i in self.interfaces:
57             i.admin_up()
58             i.config_ip4()
59             i.config_ip6()
60             i.disable_ipv6_ra()
61             i.resolve_arp()
62             i.resolve_ndp()
63
64     def tearDown(self):
65         super(TestIPIP, self).tearDown()
66         if not self.vpp_dead:
67             for i in self.pg_interfaces:
68                 i.unconfig_ip4()
69                 i.unconfig_ip6()
70                 i.admin_down()
71
72     def validate(self, rx, expected):
73         self.assertEqual(rx, expected.__class__(expected))
74
75     def generate_ip4_frags(self, payload_length, fragment_size):
76         p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
77         p_payload = UDP(sport=1234, dport=1234) / self.payload(payload_length)
78         p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
79         outer_ip4 = (p_ether / IP(src=self.pg1.remote_ip4,
80                                   id=RandShort(),
81                                   dst=self.pg0.local_ip4) / p_ip4 / p_payload)
82         frags = fragment(outer_ip4, fragment_size)
83         p4_reply = (p_ip4 / p_payload)
84         p4_reply.ttl -= 1
85         return frags, p4_reply
86
87     def verify_ip4ip4_encaps(self, a, p_ip4s, p_ip4_encaps):
88         for i, p_ip4 in enumerate(p_ip4s):
89             p_ip4.dst = a
90             p4 = (self.p_ether / p_ip4 / self.p_payload)
91             p_ip4_inner = p_ip4
92             p_ip4_inner.ttl -= 1
93             p4_reply = (p_ip4_encaps[i] / p_ip4_inner / self.p_payload)
94             p4_reply.ttl -= 1
95             p4_reply.id = 0
96             rx = self.send_and_expect(self.pg0, p4 * N_PACKETS, self.pg1)
97             for p in rx:
98                 self.validate(p[1], p4_reply)
99                 self.assert_packet_checksums_valid(p)
100
101     def verify_ip6ip4_encaps(self, a, p_ip6s, p_ip4_encaps):
102         for i, p_ip6 in enumerate(p_ip6s):
103             p_ip6.dst = a
104             p6 = (self.p_ether / p_ip6 / self.p_payload)
105             p_inner_ip6 = p_ip6
106             p_inner_ip6.hlim -= 1
107             p6_reply = (p_ip4_encaps[i] / p_inner_ip6 / self.p_payload)
108             p6_reply.ttl -= 1
109             rx = self.send_and_expect(self.pg0, p6 * N_PACKETS, self.pg1)
110             for p in rx:
111                 self.validate(p[1], p6_reply)
112                 self.assert_packet_checksums_valid(p)
113
114     def test_ipip4(self):
115         """ ip{v4,v6} over ip4 test """
116
117         self.pg1.generate_remote_hosts(5)
118         self.pg1.configure_ipv4_neighbors()
119         e = VppEnum.vl_api_tunnel_encap_decap_flags_t
120         d = VppEnum.vl_api_ip_dscp_t
121         self.p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
122         self.p_payload = UDP(sport=1234, dport=1234) / Raw(b'X' * 100)
123
124         # create a TOS byte by shifting a DSCP code point 2 bits. those 2 bits
125         # are for the ECN.
126         dscp = d.IP_API_DSCP_AF31 << 2
127         ecn = 3
128         dscp_ecn = d.IP_API_DSCP_AF31 << 2 | ecn
129
130         # IPv4 transport that copies the DCSP from the payload
131         tun_dscp = VppIpIpTunInterface(
132             self,
133             self.pg0,
134             self.pg0.local_ip4,
135             self.pg1.remote_hosts[0].ip4,
136             flags=e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP)
137         tun_dscp.add_vpp_config()
138         # IPv4 transport that copies the DCSP and ECN from the payload
139         tun_dscp_ecn = VppIpIpTunInterface(
140             self,
141             self.pg0,
142             self.pg0.local_ip4,
143             self.pg1.remote_hosts[1].ip4,
144             flags=(e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP |
145                    e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN))
146         tun_dscp_ecn.add_vpp_config()
147         # IPv4 transport that copies the ECN from the payload and sets the
148         # DF bit on encap. copies the ECN on decap
149         tun_ecn = VppIpIpTunInterface(
150             self,
151             self.pg0,
152             self.pg0.local_ip4,
153             self.pg1.remote_hosts[2].ip4,
154             flags=(e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN |
155                    e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF |
156                    e.TUNNEL_API_ENCAP_DECAP_FLAG_DECAP_COPY_ECN))
157         tun_ecn.add_vpp_config()
158         # IPv4 transport that sets a fixed DSCP in the encap and copies
159         # the DF bit
160         tun = VppIpIpTunInterface(
161             self,
162             self.pg0,
163             self.pg0.local_ip4,
164             self.pg1.remote_hosts[3].ip4,
165             dscp=d.IP_API_DSCP_AF11,
166             flags=e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF)
167         tun.add_vpp_config()
168
169         # array of all the tunnels
170         tuns = [tun_dscp, tun_dscp_ecn, tun_ecn, tun]
171
172         # addresses for prefixes routed via each tunnel
173         a4s = ["" for i in range(len(tuns))]
174         a6s = ["" for i in range(len(tuns))]
175
176         # IP headers with each combination of DSCp/ECN tested
177         p_ip6s = [IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=dscp),
178                   IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=dscp_ecn),
179                   IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=ecn),
180                   IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=0xff)]
181         p_ip4s = [IP(src="1.2.3.4", dst="130.67.0.1", tos=dscp, flags='DF'),
182                   IP(src="1.2.3.4", dst="130.67.0.1", tos=dscp_ecn),
183                   IP(src="1.2.3.4", dst="130.67.0.1", tos=ecn),
184                   IP(src="1.2.3.4", dst="130.67.0.1", tos=0xff)]
185
186         # Configure each tunnel
187         for i, t in enumerate(tuns):
188             # Set interface up and enable IP on it
189             self.vapi.sw_interface_set_flags(t.sw_if_index, 1)
190             self.vapi.sw_interface_set_unnumbered(
191                 sw_if_index=self.pg0.sw_if_index,
192                 unnumbered_sw_if_index=t.sw_if_index)
193
194             # prefix for route / destination address for packets
195             a4s[i] = "130.67.%d.0" % i
196             a6s[i] = "dead:%d::" % i
197
198             # Add IPv4 and IPv6 routes via tunnel interface
199             ip4_via_tunnel = VppIpRoute(
200                 self, a4s[i], 24,
201                 [VppRoutePath("0.0.0.0",
202                               t.sw_if_index,
203                               proto=FibPathProto.FIB_PATH_NH_PROTO_IP4)])
204             ip4_via_tunnel.add_vpp_config()
205
206             ip6_via_tunnel = VppIpRoute(
207                 self, a6s[i], 64,
208                 [VppRoutePath("::",
209                               t.sw_if_index,
210                               proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)])
211             ip6_via_tunnel.add_vpp_config()
212
213         #
214         # Encapsulation
215         #
216
217         # tun_dscp copies only the dscp
218         # expected TC values are thus only the DCSP value is present from the
219         # inner
220         exp_tcs = [dscp, dscp, 0, 0xfc]
221         p_ip44_encaps = [IP(src=self.pg0.local_ip4,
222                             dst=tun_dscp.dst,
223                             tos=tc) for tc in exp_tcs]
224         p_ip64_encaps = [IP(src=self.pg0.local_ip4,
225                             dst=tun_dscp.dst,
226                             proto='ipv6', id=0, tos=tc) for tc in exp_tcs]
227
228         # IPv4 in to IPv4 tunnel
229         self.verify_ip4ip4_encaps(a4s[0], p_ip4s, p_ip44_encaps)
230         # IPv6 in to IPv4 tunnel
231         self.verify_ip6ip4_encaps(a6s[0], p_ip6s, p_ip64_encaps)
232
233         # tun_dscp_ecn copies the dscp and the ecn
234         exp_tcs = [dscp, dscp_ecn, ecn, 0xff]
235         p_ip44_encaps = [IP(src=self.pg0.local_ip4,
236                             dst=tun_dscp_ecn.dst,
237                             tos=tc) for tc in exp_tcs]
238         p_ip64_encaps = [IP(src=self.pg0.local_ip4,
239                             dst=tun_dscp_ecn.dst,
240                             proto='ipv6', id=0, tos=tc) for tc in exp_tcs]
241
242         self.verify_ip4ip4_encaps(a4s[1], p_ip4s, p_ip44_encaps)
243         self.verify_ip6ip4_encaps(a6s[1], p_ip6s, p_ip64_encaps)
244
245         # tun_ecn copies only the ecn and always sets DF
246         exp_tcs = [0, ecn, ecn, ecn]
247         p_ip44_encaps = [IP(src=self.pg0.local_ip4,
248                             dst=tun_ecn.dst,
249                             flags='DF', tos=tc) for tc in exp_tcs]
250         p_ip64_encaps = [IP(src=self.pg0.local_ip4,
251                             dst=tun_ecn.dst,
252                             flags='DF', proto='ipv6', id=0, tos=tc)
253                          for tc in exp_tcs]
254
255         self.verify_ip4ip4_encaps(a4s[2], p_ip4s, p_ip44_encaps)
256         self.verify_ip6ip4_encaps(a6s[2], p_ip6s, p_ip64_encaps)
257
258         # tun sets a fixed dscp and copies DF
259         fixed_dscp = tun.dscp << 2
260         flags = ['DF', 0, 0, 0]
261         p_ip44_encaps = [IP(src=self.pg0.local_ip4,
262                             dst=tun.dst,
263                             flags=f,
264                             tos=fixed_dscp) for f in flags]
265         p_ip64_encaps = [IP(src=self.pg0.local_ip4,
266                             dst=tun.dst,
267                             proto='ipv6', id=0,
268                             tos=fixed_dscp) for i in range(len(p_ip4s))]
269
270         self.verify_ip4ip4_encaps(a4s[3], p_ip4s, p_ip44_encaps)
271         self.verify_ip6ip4_encaps(a6s[3], p_ip6s, p_ip64_encaps)
272
273         #
274         # Decapsulation
275         #
276         n_packets_decapped = 0
277         self.p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
278
279         # IPv4 tunnel to IPv4
280         tcs = [0, dscp, dscp_ecn, ecn]
281
282         # one overlay packet and all combinations of its encap
283         p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
284         p_ip4_encaps = [IP(src=tun.dst,
285                            dst=self.pg0.local_ip4,
286                            tos=tc) for tc in tcs]
287
288         # for each encap tun will produce the same inner packet because it does
289         # not copy up fields from the payload
290         for p_ip4_encap in p_ip4_encaps:
291             p4 = (self.p_ether / p_ip4_encap / p_ip4 / self.p_payload)
292             p4_reply = (p_ip4 / self.p_payload)
293             p4_reply.ttl -= 1
294             rx = self.send_and_expect(self.pg1, p4 * N_PACKETS, self.pg0)
295             n_packets_decapped += N_PACKETS
296             for p in rx:
297                 self.validate(p[1], p4_reply)
298                 self.assert_packet_checksums_valid(p)
299
300         err = self.statistics.get_err_counter(
301             '/err/ipip4-input/packets decapsulated')
302         self.assertEqual(err, n_packets_decapped)
303
304         # tun_ecn copies the ECN bits from the encap to the inner
305         p_ip4_encaps = [IP(src=tun_ecn.dst,
306                            dst=self.pg0.local_ip4,
307                            tos=tc) for tc in tcs]
308         p_ip4_replys = [p_ip4.copy() for i in range(len(p_ip4_encaps))]
309         p_ip4_replys[2].tos = ecn
310         p_ip4_replys[3].tos = ecn
311         for i, p_ip4_encap in enumerate(p_ip4_encaps):
312             p4 = (self.p_ether / p_ip4_encap / p_ip4 / self.p_payload)
313             p4_reply = (p_ip4_replys[i] / self.p_payload)
314             p4_reply.ttl -= 1
315             rx = self.send_and_expect(self.pg1, p4 * N_PACKETS, self.pg0)
316             n_packets_decapped += N_PACKETS
317             for p in rx:
318                 self.validate(p[1], p4_reply)
319                 self.assert_packet_checksums_valid(p)
320
321         err = self.statistics.get_err_counter(
322             '/err/ipip4-input/packets decapsulated')
323         self.assertEqual(err, n_packets_decapped)
324
325         # IPv4 tunnel to IPv6
326         # for each encap tun will produce the same inner packet because it does
327         # not copy up fields from the payload
328         p_ip4_encaps = [IP(src=tun.dst,
329                            dst=self.pg0.local_ip4,
330                            tos=tc) for tc in tcs]
331         p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
332         for p_ip4_encap in p_ip4_encaps:
333             p6 = (self.p_ether /
334                   p_ip4_encap / p_ip6 /
335                   self.p_payload)
336             p6_reply = (p_ip6 / self.p_payload)
337             p6_reply.hlim = 63
338             rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0)
339             n_packets_decapped += N_PACKETS
340             for p in rx:
341                 self.validate(p[1], p6_reply)
342                 self.assert_packet_checksums_valid(p)
343
344         err = self.statistics.get_err_counter(
345             '/err/ipip4-input/packets decapsulated')
346         self.assertEqual(err, n_packets_decapped)
347
348         # IPv4 tunnel to IPv6
349         # tun_ecn copies the ECN bits from the encap to the inner
350         p_ip4_encaps = [IP(src=tun_ecn.dst,
351                            dst=self.pg0.local_ip4,
352                            tos=tc) for tc in tcs]
353         p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
354         p_ip6_replys = [p_ip6.copy() for i in range(len(p_ip4_encaps))]
355         p_ip6_replys[2].tc = ecn
356         p_ip6_replys[3].tc = ecn
357         for i, p_ip4_encap in enumerate(p_ip4_encaps):
358             p6 = (self.p_ether / p_ip4_encap / p_ip6 / self.p_payload)
359             p6_reply = (p_ip6_replys[i] / self.p_payload)
360             p6_reply.hlim = 63
361             rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0)
362             n_packets_decapped += N_PACKETS
363             for p in rx:
364                 self.validate(p[1], p6_reply)
365                 self.assert_packet_checksums_valid(p)
366
367         err = self.statistics.get_err_counter(
368             '/err/ipip4-input/packets decapsulated')
369         self.assertEqual(err, n_packets_decapped)
370
371         #
372         # Fragmentation / Reassembly and Re-fragmentation
373         #
374         rv = self.vapi.ip_reassembly_enable_disable(
375             sw_if_index=self.pg1.sw_if_index,
376             enable_ip4=1)
377
378         self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=1000,
379                                     max_reassembly_length=1000,
380                                     expire_walk_interval_ms=10000,
381                                     is_ip6=0)
382
383         # Send lots of fragments, verify reassembled packet
384         frags, p4_reply = self.generate_ip4_frags(3131, 1400)
385         f = []
386         for i in range(0, 1000):
387             f.extend(frags)
388         self.pg1.add_stream(f)
389         self.pg_enable_capture()
390         self.pg_start()
391         rx = self.pg0.get_capture(1000)
392         n_packets_decapped += 1000
393
394         for p in rx:
395             self.validate(p[1], p4_reply)
396
397         err = self.statistics.get_err_counter(
398             '/err/ipip4-input/packets decapsulated')
399         self.assertEqual(err, n_packets_decapped)
400
401         f = []
402         r = []
403         for i in range(1, 90):
404             frags, p4_reply = self.generate_ip4_frags(i * 100, 1000)
405             f.extend(frags)
406             r.extend(p4_reply)
407         self.pg_enable_capture()
408         self.pg1.add_stream(f)
409         self.pg_start()
410         rx = self.pg0.get_capture(89)
411         i = 0
412         for p in rx:
413             self.validate(p[1], r[i])
414             i += 1
415
416         # Now try with re-fragmentation
417         #
418         # Send fragments to tunnel head-end, for the tunnel head end
419         # to reassemble and then refragment
420         #
421         self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [576, 0, 0, 0])
422         frags, p4_reply = self.generate_ip4_frags(3123, 1200)
423         self.pg_enable_capture()
424         self.pg1.add_stream(frags)
425         self.pg_start()
426         rx = self.pg0.get_capture(6)
427         reass_pkt = reassemble4(rx)
428         p4_reply.id = 256
429         self.validate(reass_pkt, p4_reply)
430
431         self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [1600, 0, 0, 0])
432         frags, p4_reply = self.generate_ip4_frags(3123, 1200)
433         self.pg_enable_capture()
434         self.pg1.add_stream(frags)
435         self.pg_start()
436         rx = self.pg0.get_capture(2)
437         reass_pkt = reassemble4(rx)
438         p4_reply.id = 512
439         self.validate(reass_pkt, p4_reply)
440
441         # send large packets through the tunnel, expect them to be fragmented
442         self.vapi.sw_interface_set_mtu(tun_dscp.sw_if_index, [600, 0, 0, 0])
443
444         p4 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
445               IP(src="1.2.3.4", dst="130.67.0.1", tos=42) /
446               UDP(sport=1234, dport=1234) / Raw(b'Q' * 1000))
447         rx = self.send_and_expect(self.pg0, p4 * 15, self.pg1, 30)
448         inners = []
449         for p in rx:
450             inners.append(p[IP].payload)
451         reass_pkt = reassemble4(inners)
452         for p in reass_pkt:
453             self.assert_packet_checksums_valid(p)
454             self.assertEqual(p[IP].ttl, 63)
455
456     def test_ipip_create(self):
457         """ ipip create / delete interface test """
458         rv = ipip_add_tunnel(self, '1.2.3.4', '2.3.4.5')
459         sw_if_index = rv.sw_if_index
460         self.vapi.ipip_del_tunnel(sw_if_index)
461
462     def test_ipip_vrf_create(self):
463         """ ipip create / delete interface VRF test """
464
465         t = VppIpTable(self, 20)
466         t.add_vpp_config()
467         rv = ipip_add_tunnel(self, '1.2.3.4', '2.3.4.5', table_id=20)
468         sw_if_index = rv.sw_if_index
469         self.vapi.ipip_del_tunnel(sw_if_index)
470
471     def payload(self, len):
472         return 'x' * len
473
474
475 class TestIPIP6(VppTestCase):
476     """ IPIP6 Test Case """
477
478     @classmethod
479     def setUpClass(cls):
480         super(TestIPIP6, cls).setUpClass()
481         cls.create_pg_interfaces(range(2))
482         cls.interfaces = list(cls.pg_interfaces)
483
484     @classmethod
485     def tearDownClass(cls):
486         super(TestIPIP6, cls).tearDownClass()
487
488     def setUp(self):
489         super(TestIPIP6, self).setUp()
490         for i in self.interfaces:
491             i.admin_up()
492             i.config_ip4()
493             i.config_ip6()
494             i.disable_ipv6_ra()
495             i.resolve_arp()
496             i.resolve_ndp()
497         self.setup_tunnel()
498
499     def tearDown(self):
500         if not self.vpp_dead:
501             self.destroy_tunnel()
502             for i in self.pg_interfaces:
503                 i.unconfig_ip4()
504                 i.unconfig_ip6()
505                 i.admin_down()
506             super(TestIPIP6, self).tearDown()
507
508     def setup_tunnel(self):
509         # IPv6 transport
510         rv = ipip_add_tunnel(self,
511                              self.pg0.local_ip6,
512                              self.pg1.remote_ip6)
513
514         sw_if_index = rv.sw_if_index
515         self.tunnel_if_index = sw_if_index
516         self.vapi.sw_interface_set_flags(sw_if_index, 1)
517         self.vapi.sw_interface_set_unnumbered(
518             sw_if_index=self.pg0.sw_if_index,
519             unnumbered_sw_if_index=sw_if_index)
520
521         # Add IPv4 and IPv6 routes via tunnel interface
522         ip4_via_tunnel = VppIpRoute(
523             self, "130.67.0.0", 16,
524             [VppRoutePath("0.0.0.0",
525                           sw_if_index,
526                           proto=FibPathProto.FIB_PATH_NH_PROTO_IP4)])
527         ip4_via_tunnel.add_vpp_config()
528
529         ip6_via_tunnel = VppIpRoute(
530             self, "dead::", 16,
531             [VppRoutePath("::",
532                           sw_if_index,
533                           proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)])
534         ip6_via_tunnel.add_vpp_config()
535
536         self.tunnel_ip6_via_tunnel = ip6_via_tunnel
537         self.tunnel_ip4_via_tunnel = ip4_via_tunnel
538
539     def destroy_tunnel(self):
540         # IPv6 transport
541         self.tunnel_ip4_via_tunnel.remove_vpp_config()
542         self.tunnel_ip6_via_tunnel.remove_vpp_config()
543
544         rv = self.vapi.ipip_del_tunnel(sw_if_index=self.tunnel_if_index)
545
546     def validate(self, rx, expected):
547         self.assertEqual(rx, expected.__class__(expected))
548
549     def generate_ip6_frags(self, payload_length, fragment_size):
550         p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
551         p_payload = UDP(sport=1234, dport=1234) / self.payload(payload_length)
552         p_ip6 = IPv6(src="1::1", dst=self.pg0.remote_ip6)
553         outer_ip6 = (p_ether / IPv6(src=self.pg1.remote_ip6,
554                                     dst=self.pg0.local_ip6) /
555                      IPv6ExtHdrFragment() / p_ip6 / p_payload)
556         frags = fragment6(outer_ip6, fragment_size)
557         p6_reply = (p_ip6 / p_payload)
558         p6_reply.hlim -= 1
559         return frags, p6_reply
560
561     def generate_ip6_hairpin_frags(self, payload_length, fragment_size):
562         p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
563         p_payload = UDP(sport=1234, dport=1234) / self.payload(payload_length)
564         p_ip6 = IPv6(src="1::1", dst="dead::1")
565         outer_ip6 = (p_ether / IPv6(src=self.pg1.remote_ip6,
566                                     dst=self.pg0.local_ip6) /
567                      IPv6ExtHdrFragment() / p_ip6 / p_payload)
568         frags = fragment6(outer_ip6, fragment_size)
569         p_ip6.hlim -= 1
570         p6_reply = (IPv6(src=self.pg0.local_ip6, dst=self.pg1.remote_ip6,
571                          hlim=63) / p_ip6 / p_payload)
572
573         return frags, p6_reply
574
575     def test_encap(self):
576         """ ip{v4,v6} over ip6 test encap """
577         p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
578         p_ip6 = IPv6(src="1::1", dst="DEAD::1", tc=42, nh='UDP')
579         p_ip4 = IP(src="1.2.3.4", dst="130.67.0.1", tos=42)
580         p_payload = UDP(sport=1234, dport=1234)
581
582         # Encapsulation
583         # IPv6 in to IPv6 tunnel
584         p6 = (p_ether / p_ip6 / p_payload)
585         p6_reply = (IPv6(src=self.pg0.local_ip6, dst=self.pg1.remote_ip6,
586                          hlim=64) /
587                     p_ip6 / p_payload)
588         p6_reply[1].hlim -= 1
589         rx = self.send_and_expect(self.pg0, p6 * 11, self.pg1)
590         for p in rx:
591             self.validate(p[1], p6_reply)
592
593         # IPv4 in to IPv6 tunnel
594         p4 = (p_ether / p_ip4 / p_payload)
595         p4_reply = (IPv6(src=self.pg0.local_ip6,
596                          dst=self.pg1.remote_ip6, hlim=64) /
597                     p_ip4 / p_payload)
598         p4_reply[1].ttl -= 1
599         rx = self.send_and_expect(self.pg0, p4 * 11, self.pg1)
600         for p in rx:
601             self.validate(p[1], p4_reply)
602
603     def test_decap(self):
604         """ ip{v4,v6} over ip6 test decap """
605
606         p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
607         p_ip6 = IPv6(src="1::1", dst="DEAD::1", tc=42, nh='UDP')
608         p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
609         p_payload = UDP(sport=1234, dport=1234)
610
611         # Decapsulation
612         # IPv6 tunnel to IPv4
613
614         p4 = (p_ether / IPv6(src=self.pg1.remote_ip6,
615                              dst=self.pg0.local_ip6) / p_ip4 / p_payload)
616         p4_reply = (p_ip4 / p_payload)
617         p4_reply.ttl -= 1
618         rx = self.send_and_expect(self.pg1, p4 * 11, self.pg0)
619         for p in rx:
620             self.validate(p[1], p4_reply)
621
622         # IPv6 tunnel to IPv6
623         p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
624         p6 = (p_ether / IPv6(src=self.pg1.remote_ip6,
625                              dst=self.pg0.local_ip6) / p_ip6 / p_payload)
626         p6_reply = (p_ip6 / p_payload)
627         p6_reply.hlim = 63
628         rx = self.send_and_expect(self.pg1, p6 * 11, self.pg0)
629         for p in rx:
630             self.validate(p[1], p6_reply)
631
632     def verify_ip4ip6_encaps(self, a, p_ip4s, p_ip6_encaps):
633         for i, p_ip4 in enumerate(p_ip4s):
634             p_ip4.dst = a
635             p4 = (self.p_ether / p_ip4 / self.p_payload)
636             p_ip4_inner = p_ip4
637             p_ip4_inner.ttl -= 1
638             p6_reply = (p_ip6_encaps[i] / p_ip4_inner / self.p_payload)
639             rx = self.send_and_expect(self.pg0, p4 * N_PACKETS, self.pg1)
640             for p in rx:
641                 self.validate(p[1], p6_reply)
642                 self.assert_packet_checksums_valid(p)
643
644     def verify_ip6ip6_encaps(self, a, p_ip6s, p_ip6_encaps):
645         for i, p_ip6 in enumerate(p_ip6s):
646             p_ip6.dst = a
647             p6 = (self.p_ether / p_ip6 / self.p_payload)
648             p_inner_ip6 = p_ip6
649             p_inner_ip6.hlim -= 1
650             p6_reply = (p_ip6_encaps[i] / p_inner_ip6 / self.p_payload)
651             rx = self.send_and_expect(self.pg0, p6 * N_PACKETS, self.pg1)
652             for p in rx:
653                 self.validate(p[1], p6_reply)
654                 self.assert_packet_checksums_valid(p)
655
656     def test_ipip6(self):
657         """ ip{v4,v6} over ip6 test """
658
659         # that's annoying
660         self.destroy_tunnel()
661
662         self.pg1.generate_remote_hosts(5)
663         self.pg1.configure_ipv6_neighbors()
664         e = VppEnum.vl_api_tunnel_encap_decap_flags_t
665         d = VppEnum.vl_api_ip_dscp_t
666         self.p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
667         self.p_payload = UDP(sport=1234, dport=1234) / Raw(b'X' * 100)
668
669         # create a TOS byte by shifting a DSCP code point 2 bits. those 2 bits
670         # are for the ECN.
671         dscp = d.IP_API_DSCP_AF31 << 2
672         ecn = 3
673         dscp_ecn = d.IP_API_DSCP_AF31 << 2 | ecn
674
675         # IPv4 transport that copies the DCSP from the payload
676         tun_dscp = VppIpIpTunInterface(
677             self,
678             self.pg0,
679             self.pg0.local_ip6,
680             self.pg1.remote_hosts[0].ip6,
681             flags=e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP)
682         tun_dscp.add_vpp_config()
683         # IPv4 transport that copies the DCSP and ECN from the payload
684         tun_dscp_ecn = VppIpIpTunInterface(
685             self,
686             self.pg0,
687             self.pg0.local_ip6,
688             self.pg1.remote_hosts[1].ip6,
689             flags=(e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP |
690                    e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN))
691         tun_dscp_ecn.add_vpp_config()
692         # IPv4 transport that copies the ECN from the payload and sets the
693         # DF bit on encap. copies the ECN on decap
694         tun_ecn = VppIpIpTunInterface(
695             self,
696             self.pg0,
697             self.pg0.local_ip6,
698             self.pg1.remote_hosts[2].ip6,
699             flags=(e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN |
700                    e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF |
701                    e.TUNNEL_API_ENCAP_DECAP_FLAG_DECAP_COPY_ECN))
702         tun_ecn.add_vpp_config()
703         # IPv4 transport that sets a fixed DSCP in the encap and copies
704         # the DF bit
705         tun = VppIpIpTunInterface(
706             self,
707             self.pg0,
708             self.pg0.local_ip6,
709             self.pg1.remote_hosts[3].ip6,
710             dscp=d.IP_API_DSCP_AF11,
711             flags=e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF)
712         tun.add_vpp_config()
713
714         # array of all the tunnels
715         tuns = [tun_dscp, tun_dscp_ecn, tun_ecn, tun]
716
717         # addresses for prefixes routed via each tunnel
718         a4s = ["" for i in range(len(tuns))]
719         a6s = ["" for i in range(len(tuns))]
720
721         # IP headers for inner packets with each combination of DSCp/ECN tested
722         p_ip6s = [IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=dscp),
723                   IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=dscp_ecn),
724                   IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=ecn),
725                   IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=0xff)]
726         p_ip4s = [IP(src="1.2.3.4", dst="130.67.0.1", tos=dscp, flags='DF'),
727                   IP(src="1.2.3.4", dst="130.67.0.1", tos=dscp_ecn),
728                   IP(src="1.2.3.4", dst="130.67.0.1", tos=ecn),
729                   IP(src="1.2.3.4", dst="130.67.0.1", tos=0xff)]
730
731         # Configure each tunnel
732         for i, t in enumerate(tuns):
733             # Set interface up and enable IP on it
734             self.vapi.sw_interface_set_flags(t.sw_if_index, 1)
735             self.vapi.sw_interface_set_unnumbered(
736                 sw_if_index=self.pg0.sw_if_index,
737                 unnumbered_sw_if_index=t.sw_if_index)
738
739             # prefix for route / destination address for packets
740             a4s[i] = "130.67.%d.0" % i
741             a6s[i] = "dead:%d::" % i
742
743             # Add IPv4 and IPv6 routes via tunnel interface
744             ip4_via_tunnel = VppIpRoute(
745                 self, a4s[i], 24,
746                 [VppRoutePath("0.0.0.0",
747                               t.sw_if_index,
748                               proto=FibPathProto.FIB_PATH_NH_PROTO_IP4)])
749             ip4_via_tunnel.add_vpp_config()
750
751             ip6_via_tunnel = VppIpRoute(
752                 self, a6s[i], 64,
753                 [VppRoutePath("::",
754                               t.sw_if_index,
755                               proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)])
756             ip6_via_tunnel.add_vpp_config()
757
758         #
759         # Encapsulation
760         #
761
762         # tun_dscp copies only the dscp
763         # expected TC values are thus only the DCSP value is present from the
764         # inner
765         exp_tcs = [dscp, dscp, 0, 0xfc]
766         p_ip6_encaps = [IPv6(src=self.pg0.local_ip6,
767                              dst=tun_dscp.dst,
768                              tc=tc) for tc in exp_tcs]
769
770         # IPv4 in to IPv4 tunnel
771         self.verify_ip4ip6_encaps(a4s[0], p_ip4s, p_ip6_encaps)
772         # IPv6 in to IPv4 tunnel
773         self.verify_ip6ip6_encaps(a6s[0], p_ip6s, p_ip6_encaps)
774
775         # tun_dscp_ecn copies the dscp and the ecn
776         exp_tcs = [dscp, dscp_ecn, ecn, 0xff]
777         p_ip6_encaps = [IPv6(src=self.pg0.local_ip6,
778                              dst=tun_dscp_ecn.dst,
779                              tc=tc) for tc in exp_tcs]
780
781         self.verify_ip4ip6_encaps(a4s[1], p_ip4s, p_ip6_encaps)
782         self.verify_ip6ip6_encaps(a6s[1], p_ip6s, p_ip6_encaps)
783
784         # tun_ecn copies only the ecn and always sets DF
785         exp_tcs = [0, ecn, ecn, ecn]
786         p_ip6_encaps = [IPv6(src=self.pg0.local_ip6,
787                              dst=tun_ecn.dst,
788                              tc=tc) for tc in exp_tcs]
789
790         self.verify_ip4ip6_encaps(a4s[2], p_ip4s, p_ip6_encaps)
791         self.verify_ip6ip6_encaps(a6s[2], p_ip6s, p_ip6_encaps)
792
793         # tun sets a fixed dscp
794         fixed_dscp = tun.dscp << 2
795         p_ip6_encaps = [IPv6(src=self.pg0.local_ip6,
796                              dst=tun.dst,
797                              tc=fixed_dscp) for i in range(len(p_ip4s))]
798
799         self.verify_ip4ip6_encaps(a4s[3], p_ip4s, p_ip6_encaps)
800         self.verify_ip6ip6_encaps(a6s[3], p_ip6s, p_ip6_encaps)
801
802         #
803         # Decapsulation
804         #
805         n_packets_decapped = self.statistics.get_err_counter(
806             '/err/ipip6-input/packets decapsulated')
807
808         self.p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
809
810         # IPv6 tunnel to IPv4
811         tcs = [0, dscp, dscp_ecn, ecn]
812
813         # one overlay packet and all combinations of its encap
814         p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
815         p_ip6_encaps = [IPv6(src=tun.dst,
816                              dst=self.pg0.local_ip6,
817                              tc=tc) for tc in tcs]
818
819         # for each encap tun will produce the same inner packet because it does
820         # not copy up fields from the payload
821         for p_ip6_encap in p_ip6_encaps:
822             p6 = (self.p_ether / p_ip6_encap / p_ip4 / self.p_payload)
823             p4_reply = (p_ip4 / self.p_payload)
824             p4_reply.ttl -= 1
825             rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0)
826             n_packets_decapped += N_PACKETS
827             for p in rx:
828                 self.validate(p[1], p4_reply)
829                 self.assert_packet_checksums_valid(p)
830
831         err = self.statistics.get_err_counter(
832             '/err/ipip6-input/packets decapsulated')
833         self.assertEqual(err, n_packets_decapped)
834
835         # tun_ecn copies the ECN bits from the encap to the inner
836         p_ip6_encaps = [IPv6(src=tun_ecn.dst,
837                              dst=self.pg0.local_ip6,
838                              tc=tc) for tc in tcs]
839         p_ip4_replys = [p_ip4.copy() for i in range(len(p_ip6_encaps))]
840         p_ip4_replys[2].tos = ecn
841         p_ip4_replys[3].tos = ecn
842         for i, p_ip6_encap in enumerate(p_ip6_encaps):
843             p6 = (self.p_ether / p_ip6_encap / p_ip4 / self.p_payload)
844             p4_reply = (p_ip4_replys[i] / self.p_payload)
845             p4_reply.ttl -= 1
846             rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0)
847             n_packets_decapped += N_PACKETS
848             for p in rx:
849                 self.validate(p[1], p4_reply)
850                 self.assert_packet_checksums_valid(p)
851
852         err = self.statistics.get_err_counter(
853             '/err/ipip6-input/packets decapsulated')
854         self.assertEqual(err, n_packets_decapped)
855
856         # IPv6 tunnel to IPv6
857         # for each encap tun will produce the same inner packet because it does
858         # not copy up fields from the payload
859         p_ip6_encaps = [IPv6(src=tun.dst,
860                              dst=self.pg0.local_ip6,
861                              tc=tc) for tc in tcs]
862         p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
863         for p_ip6_encap in p_ip6_encaps:
864             p6 = (self.p_ether / p_ip6_encap / p_ip6 / self.p_payload)
865             p6_reply = (p_ip6 / self.p_payload)
866             p6_reply.hlim = 63
867             rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0)
868             n_packets_decapped += N_PACKETS
869             for p in rx:
870                 self.validate(p[1], p6_reply)
871                 self.assert_packet_checksums_valid(p)
872
873         err = self.statistics.get_err_counter(
874             '/err/ipip6-input/packets decapsulated')
875         self.assertEqual(err, n_packets_decapped)
876
877         # IPv6 tunnel to IPv6
878         # tun_ecn copies the ECN bits from the encap to the inner
879         p_ip6_encaps = [IPv6(src=tun_ecn.dst,
880                              dst=self.pg0.local_ip6,
881                              tc=tc) for tc in tcs]
882         p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
883         p_ip6_replys = [p_ip6.copy() for i in range(len(p_ip6_encaps))]
884         p_ip6_replys[2].tc = ecn
885         p_ip6_replys[3].tc = ecn
886         for i, p_ip6_encap in enumerate(p_ip6_encaps):
887             p6 = (self.p_ether / p_ip6_encap / p_ip6 / self.p_payload)
888             p6_reply = (p_ip6_replys[i] / self.p_payload)
889             p6_reply.hlim = 63
890             rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0)
891             n_packets_decapped += N_PACKETS
892             for p in rx:
893                 self.validate(p[1], p6_reply)
894                 self.assert_packet_checksums_valid(p)
895
896         err = self.statistics.get_err_counter(
897             '/err/ipip6-input/packets decapsulated')
898         self.assertEqual(err, n_packets_decapped)
899
900     def test_frag(self):
901         """ ip{v4,v6} over ip6 test frag """
902
903         p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
904         p_ip6 = IPv6(src="1::1", dst="DEAD::1", tc=42, nh='UDP')
905         p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
906         p_payload = UDP(sport=1234, dport=1234)
907
908         #
909         # Fragmentation / Reassembly and Re-fragmentation
910         #
911         rv = self.vapi.ip_reassembly_enable_disable(
912             sw_if_index=self.pg1.sw_if_index,
913             enable_ip6=1)
914
915         self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=1000,
916                                     max_reassembly_length=1000,
917                                     expire_walk_interval_ms=10000,
918                                     is_ip6=1)
919
920         # Send lots of fragments, verify reassembled packet
921         before_cnt = self.statistics.get_err_counter(
922             '/err/ipip6-input/packets decapsulated')
923         frags, p6_reply = self.generate_ip6_frags(3131, 1400)
924         f = []
925         for i in range(0, 1000):
926             f.extend(frags)
927         self.pg1.add_stream(f)
928         self.pg_enable_capture()
929         self.pg_start()
930         rx = self.pg0.get_capture(1000)
931
932         for p in rx:
933             self.validate(p[1], p6_reply)
934
935         cnt = self.statistics.get_err_counter(
936             '/err/ipip6-input/packets decapsulated')
937         self.assertEqual(cnt, before_cnt + 1000)
938
939         f = []
940         r = []
941         # TODO: Check out why reassembly of atomic fragments don't work
942         for i in range(10, 90):
943             frags, p6_reply = self.generate_ip6_frags(i * 100, 1000)
944             f.extend(frags)
945             r.extend(p6_reply)
946         self.pg_enable_capture()
947         self.pg1.add_stream(f)
948         self.pg_start()
949         rx = self.pg0.get_capture(80)
950         i = 0
951         for p in rx:
952             self.validate(p[1], r[i])
953             i += 1
954
955         # Simple fragmentation
956         p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
957         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1280, 0, 0, 0])
958
959         # IPv6 in to IPv6 tunnel
960         p_payload = UDP(sport=1234, dport=1234) / self.payload(1300)
961
962         p6 = (p_ether / p_ip6 / p_payload)
963         p6_reply = (IPv6(src=self.pg0.local_ip6, dst=self.pg1.remote_ip6,
964                          hlim=63) /
965                     p_ip6 / p_payload)
966         p6_reply[1].hlim -= 1
967         self.pg_enable_capture()
968         self.pg0.add_stream(p6)
969         self.pg_start()
970         rx = self.pg1.get_capture(2)
971
972         # Scapy defragment doesn't deal well with multiple layers
973         # of same type / Ethernet header first
974         f = [p[1] for p in rx]
975         reass_pkt = defragment6(f)
976         self.validate(reass_pkt, p6_reply)
977
978         # Now try with re-fragmentation
979         #
980         # Send large fragments to tunnel head-end, for the tunnel head end
981         # to reassemble and then refragment out the tunnel again.
982         # Hair-pinning
983         #
984         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1280, 0, 0, 0])
985         frags, p6_reply = self.generate_ip6_hairpin_frags(8000, 1200)
986         self.pg_enable_capture()
987         self.pg1.add_stream(frags)
988         self.pg_start()
989         rx = self.pg1.get_capture(7)
990         f = [p[1] for p in rx]
991         reass_pkt = defragment6(f)
992         p6_reply.id = 256
993         self.validate(reass_pkt, p6_reply)
994
995     def test_ipip_create(self):
996         """ ipip create / delete interface test """
997         rv = ipip_add_tunnel(self, '1.2.3.4', '2.3.4.5')
998         sw_if_index = rv.sw_if_index
999         self.vapi.ipip_del_tunnel(sw_if_index)
1000
1001     def test_ipip_vrf_create(self):
1002         """ ipip create / delete interface VRF test """
1003
1004         t = VppIpTable(self, 20)
1005         t.add_vpp_config()
1006         rv = ipip_add_tunnel(self, '1.2.3.4', '2.3.4.5', table_id=20)
1007         sw_if_index = rv.sw_if_index
1008         self.vapi.ipip_del_tunnel(sw_if_index)
1009
1010     def payload(self, len):
1011         return 'x' * len
1012
1013
1014 if __name__ == '__main__':
1015     unittest.main(testRunner=VppTestRunner)