2 """IP{4,6} over IP{v,6} tunnel functional tests"""
5 from scapy.layers.inet6 import IPv6, Ether, IP, UDP, IPv6ExtHdrFragment, Raw
6 from scapy.contrib.mpls import MPLS
7 from scapy.all import fragment, fragment6, RandShort, defragment6
8 from framework import VppTestCase, VppTestRunner
9 from vpp_ip import DpoProto
10 from vpp_ip_route import (
19 from vpp_ipip_tun_interface import VppIpIpTunInterface
20 from vpp_teib import VppTeib
21 from vpp_papi import VppEnum
22 from socket import AF_INET, AF_INET6, inet_pton
23 from util import reassemble4
25 """ Testipip is a subclass of VPPTestCase classes.
32 def ipip_add_tunnel(test, src, dst, table_id=0, dscp=0x0, flags=0):
33 """Add a IPIP tunnel"""
34 return test.vapi.ipip_add_tunnel(
39 "instance": 0xFFFFFFFF,
46 # the number of packets to send when injecting traffic.
47 # a multiple of 8 minus one, so we test all by 8/4/2/1 loops
51 class TestIPIP(VppTestCase):
56 super(TestIPIP, cls).setUpClass()
57 cls.create_pg_interfaces(range(3))
58 cls.interfaces = list(cls.pg_interfaces)
61 def tearDownClass(cls):
62 super(TestIPIP, cls).tearDownClass()
65 super(TestIPIP, self).setUp()
66 self.table = VppIpTable(self, 1, register=False)
67 self.table.add_vpp_config()
69 for i in self.interfaces:
72 self.pg2.set_table_ip4(self.table.table_id)
73 for i in self.interfaces:
81 super(TestIPIP, self).tearDown()
83 for i in self.pg_interfaces:
89 self.table.remove_vpp_config()
91 def validate(self, rx, expected):
92 self.assertEqual(rx, expected.__class__(expected))
94 def generate_ip4_frags(self, payload_length, fragment_size):
95 p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
96 p_payload = UDP(sport=1234, dport=1234) / self.payload(payload_length)
97 p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
100 / IP(src=self.pg1.remote_ip4, id=RandShort(), dst=self.pg0.local_ip4)
104 frags = fragment(outer_ip4, fragment_size)
105 p4_reply = p_ip4 / p_payload
107 return frags, p4_reply
109 def verify_ip4ip4_encaps(self, a, p_ip4s, p_ip4_encaps):
110 for i, p_ip4 in enumerate(p_ip4s):
112 p4 = self.p_ether / p_ip4 / self.p_payload
115 p4_reply = p_ip4_encaps[i] / p_ip4_inner / self.p_payload
118 rx = self.send_and_expect(self.pg0, p4 * N_PACKETS, self.pg1)
120 self.validate(p[1], p4_reply)
121 self.assert_packet_checksums_valid(p)
123 def verify_ip6ip4_encaps(self, a, p_ip6s, p_ip4_encaps):
124 for i, p_ip6 in enumerate(p_ip6s):
126 p6 = self.p_ether / p_ip6 / self.p_payload
128 p_inner_ip6.hlim -= 1
129 p6_reply = p_ip4_encaps[i] / p_inner_ip6 / self.p_payload
131 rx = self.send_and_expect(self.pg0, p6 * N_PACKETS, self.pg1)
133 self.validate(p[1], p6_reply)
134 self.assert_packet_checksums_valid(p)
136 def test_ipip4(self):
137 """ip{v4,v6} over ip4 test"""
139 self.pg1.generate_remote_hosts(5)
140 self.pg1.configure_ipv4_neighbors()
141 e = VppEnum.vl_api_tunnel_encap_decap_flags_t
142 d = VppEnum.vl_api_ip_dscp_t
143 self.p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
144 self.p_payload = UDP(sport=1234, dport=1234) / Raw(b"X" * 100)
146 # create a TOS byte by shifting a DSCP code point 2 bits. those 2 bits
148 dscp = d.IP_API_DSCP_AF31 << 2
150 dscp_ecn = d.IP_API_DSCP_AF31 << 2 | ecn
152 # IPv4 transport that copies the DCSP from the payload
153 tun_dscp = VppIpIpTunInterface(
157 self.pg1.remote_hosts[0].ip4,
158 flags=e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP,
160 tun_dscp.add_vpp_config()
161 # IPv4 transport that copies the DCSP and ECN from the payload
162 tun_dscp_ecn = VppIpIpTunInterface(
166 self.pg1.remote_hosts[1].ip4,
168 e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP
169 | e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN
172 tun_dscp_ecn.add_vpp_config()
173 # IPv4 transport that copies the ECN from the payload and sets the
174 # DF bit on encap. copies the ECN on decap
175 tun_ecn = VppIpIpTunInterface(
179 self.pg1.remote_hosts[2].ip4,
181 e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN
182 | e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF
183 | e.TUNNEL_API_ENCAP_DECAP_FLAG_DECAP_COPY_ECN
186 tun_ecn.add_vpp_config()
187 # IPv4 transport that sets a fixed DSCP in the encap and copies
189 tun = VppIpIpTunInterface(
193 self.pg1.remote_hosts[3].ip4,
194 dscp=d.IP_API_DSCP_AF11,
195 flags=e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF,
199 # array of all the tunnels
200 tuns = [tun_dscp, tun_dscp_ecn, tun_ecn, tun]
202 # addresses for prefixes routed via each tunnel
203 a4s = ["" for i in range(len(tuns))]
204 a6s = ["" for i in range(len(tuns))]
206 # IP headers with each combination of DSCp/ECN tested
208 IPv6(src="1::1", dst="DEAD::1", nh="UDP", tc=dscp),
209 IPv6(src="1::1", dst="DEAD::1", nh="UDP", tc=dscp_ecn),
210 IPv6(src="1::1", dst="DEAD::1", nh="UDP", tc=ecn),
211 IPv6(src="1::1", dst="DEAD::1", nh="UDP", tc=0xFF),
214 IP(src="1.2.3.4", dst="130.67.0.1", tos=dscp, flags="DF"),
215 IP(src="1.2.3.4", dst="130.67.0.1", tos=dscp_ecn),
216 IP(src="1.2.3.4", dst="130.67.0.1", tos=ecn),
217 IP(src="1.2.3.4", dst="130.67.0.1", tos=0xFF),
220 # Configure each tunnel
221 for i, t in enumerate(tuns):
222 # Set interface up and enable IP on it
223 self.vapi.sw_interface_set_flags(t.sw_if_index, 1)
224 self.vapi.sw_interface_set_unnumbered(
225 sw_if_index=self.pg0.sw_if_index, unnumbered_sw_if_index=t.sw_if_index
228 # prefix for route / destination address for packets
229 a4s[i] = "130.67.%d.0" % i
230 a6s[i] = "dead:%d::" % i
232 # Add IPv4 and IPv6 routes via tunnel interface
233 ip4_via_tunnel = VppIpRoute(
241 proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
245 ip4_via_tunnel.add_vpp_config()
247 ip6_via_tunnel = VppIpRoute(
253 "::", t.sw_if_index, proto=FibPathProto.FIB_PATH_NH_PROTO_IP6
257 ip6_via_tunnel.add_vpp_config()
263 # tun_dscp copies only the dscp
264 # expected TC values are thus only the DCSP value is present from the
266 exp_tcs = [dscp, dscp, 0, 0xFC]
268 IP(src=self.pg0.local_ip4, dst=tun_dscp.dst, tos=tc) for tc in exp_tcs
271 IP(src=self.pg0.local_ip4, dst=tun_dscp.dst, proto="ipv6", id=0, tos=tc)
275 # IPv4 in to IPv4 tunnel
276 self.verify_ip4ip4_encaps(a4s[0], p_ip4s, p_ip44_encaps)
277 # IPv6 in to IPv4 tunnel
278 self.verify_ip6ip4_encaps(a6s[0], p_ip6s, p_ip64_encaps)
280 # tun_dscp_ecn copies the dscp and the ecn
281 exp_tcs = [dscp, dscp_ecn, ecn, 0xFF]
283 IP(src=self.pg0.local_ip4, dst=tun_dscp_ecn.dst, tos=tc) for tc in exp_tcs
286 IP(src=self.pg0.local_ip4, dst=tun_dscp_ecn.dst, proto="ipv6", id=0, tos=tc)
290 self.verify_ip4ip4_encaps(a4s[1], p_ip4s, p_ip44_encaps)
291 self.verify_ip6ip4_encaps(a6s[1], p_ip6s, p_ip64_encaps)
293 # tun_ecn copies only the ecn and always sets DF
294 exp_tcs = [0, ecn, ecn, ecn]
296 IP(src=self.pg0.local_ip4, dst=tun_ecn.dst, flags="DF", tos=tc)
301 src=self.pg0.local_ip4,
311 self.verify_ip4ip4_encaps(a4s[2], p_ip4s, p_ip44_encaps)
312 self.verify_ip6ip4_encaps(a6s[2], p_ip6s, p_ip64_encaps)
314 # tun sets a fixed dscp and copies DF
315 fixed_dscp = tun.dscp << 2
316 flags = ["DF", 0, 0, 0]
318 IP(src=self.pg0.local_ip4, dst=tun.dst, flags=f, tos=fixed_dscp)
322 IP(src=self.pg0.local_ip4, dst=tun.dst, proto="ipv6", id=0, tos=fixed_dscp)
323 for i in range(len(p_ip4s))
326 self.verify_ip4ip4_encaps(a4s[3], p_ip4s, p_ip44_encaps)
327 self.verify_ip6ip4_encaps(a6s[3], p_ip6s, p_ip64_encaps)
332 n_packets_decapped = 0
333 self.p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
335 # IPv4 tunnel to IPv4
336 tcs = [0, dscp, dscp_ecn, ecn]
338 # one overlay packet and all combinations of its encap
339 p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
340 p_ip4_encaps = [IP(src=tun.dst, dst=self.pg0.local_ip4, tos=tc) for tc in tcs]
342 # for each encap tun will produce the same inner packet because it does
343 # not copy up fields from the payload
344 for p_ip4_encap in p_ip4_encaps:
345 p4 = self.p_ether / p_ip4_encap / p_ip4 / self.p_payload
346 p4_reply = p_ip4 / self.p_payload
348 rx = self.send_and_expect(self.pg1, p4 * N_PACKETS, self.pg0)
349 n_packets_decapped += N_PACKETS
351 self.validate(p[1], p4_reply)
352 self.assert_packet_checksums_valid(p)
354 err = self.statistics.get_err_counter("/err/ipip4-input/packets decapsulated")
355 self.assertEqual(err, n_packets_decapped)
357 # tun_ecn copies the ECN bits from the encap to the inner
359 IP(src=tun_ecn.dst, dst=self.pg0.local_ip4, tos=tc) for tc in tcs
361 p_ip4_replys = [p_ip4.copy() for i in range(len(p_ip4_encaps))]
362 p_ip4_replys[2].tos = ecn
363 p_ip4_replys[3].tos = ecn
364 for i, p_ip4_encap in enumerate(p_ip4_encaps):
365 p4 = self.p_ether / p_ip4_encap / p_ip4 / self.p_payload
366 p4_reply = p_ip4_replys[i] / self.p_payload
368 rx = self.send_and_expect(self.pg1, p4 * N_PACKETS, self.pg0)
369 n_packets_decapped += N_PACKETS
371 self.validate(p[1], p4_reply)
372 self.assert_packet_checksums_valid(p)
374 err = self.statistics.get_err_counter("/err/ipip4-input/packets decapsulated")
375 self.assertEqual(err, n_packets_decapped)
377 # IPv4 tunnel to IPv6
378 # for each encap tun will produce the same inner packet because it does
379 # not copy up fields from the payload
380 p_ip4_encaps = [IP(src=tun.dst, dst=self.pg0.local_ip4, tos=tc) for tc in tcs]
381 p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
382 for p_ip4_encap in p_ip4_encaps:
383 p6 = self.p_ether / p_ip4_encap / p_ip6 / self.p_payload
384 p6_reply = p_ip6 / self.p_payload
386 rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0)
387 n_packets_decapped += N_PACKETS
389 self.validate(p[1], p6_reply)
390 self.assert_packet_checksums_valid(p)
392 err = self.statistics.get_err_counter("/err/ipip4-input/packets decapsulated")
393 self.assertEqual(err, n_packets_decapped)
395 # IPv4 tunnel to IPv6
396 # tun_ecn copies the ECN bits from the encap to the inner
398 IP(src=tun_ecn.dst, dst=self.pg0.local_ip4, tos=tc) for tc in tcs
400 p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
401 p_ip6_replys = [p_ip6.copy() for i in range(len(p_ip4_encaps))]
402 p_ip6_replys[2].tc = ecn
403 p_ip6_replys[3].tc = ecn
404 for i, p_ip4_encap in enumerate(p_ip4_encaps):
405 p6 = self.p_ether / p_ip4_encap / p_ip6 / self.p_payload
406 p6_reply = p_ip6_replys[i] / self.p_payload
408 rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0)
409 n_packets_decapped += N_PACKETS
411 self.validate(p[1], p6_reply)
412 self.assert_packet_checksums_valid(p)
414 err = self.statistics.get_err_counter("/err/ipip4-input/packets decapsulated")
415 self.assertEqual(err, n_packets_decapped)
418 # Fragmentation / Reassembly and Re-fragmentation
420 rv = self.vapi.ip_reassembly_enable_disable(
421 sw_if_index=self.pg1.sw_if_index, enable_ip4=1
424 self.vapi.ip_reassembly_set(
426 max_reassemblies=1000,
427 max_reassembly_length=1000,
428 expire_walk_interval_ms=10000,
432 # Send lots of fragments, verify reassembled packet
433 frags, p4_reply = self.generate_ip4_frags(3131, 1400)
435 for i in range(0, 1000):
437 self.pg1.add_stream(f)
438 self.pg_enable_capture()
440 rx = self.pg0.get_capture(1000)
441 n_packets_decapped += 1000
444 self.validate(p[1], p4_reply)
446 err = self.statistics.get_err_counter("/err/ipip4-input/packets decapsulated")
447 self.assertEqual(err, n_packets_decapped)
451 for i in range(1, 90):
452 frags, p4_reply = self.generate_ip4_frags(i * 100, 1000)
455 self.pg_enable_capture()
456 self.pg1.add_stream(f)
458 rx = self.pg0.get_capture(89)
461 self.validate(p[1], r[i])
464 # Now try with re-fragmentation
466 # Send fragments to tunnel head-end, for the tunnel head end
467 # to reassemble and then refragment
469 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [576, 0, 0, 0])
470 frags, p4_reply = self.generate_ip4_frags(3123, 1200)
471 self.pg_enable_capture()
472 self.pg1.add_stream(frags)
474 rx = self.pg0.get_capture(6)
475 reass_pkt = reassemble4(rx)
477 self.validate(reass_pkt, p4_reply)
479 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [1600, 0, 0, 0])
480 frags, p4_reply = self.generate_ip4_frags(3123, 1200)
481 self.pg_enable_capture()
482 self.pg1.add_stream(frags)
484 rx = self.pg0.get_capture(2)
485 reass_pkt = reassemble4(rx)
487 self.validate(reass_pkt, p4_reply)
489 # send large packets through the tunnel, expect them to be fragmented
490 self.vapi.sw_interface_set_mtu(tun_dscp.sw_if_index, [600, 0, 0, 0])
493 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
494 / IP(src="1.2.3.4", dst="130.67.0.1", tos=42)
495 / UDP(sport=1234, dport=1234)
498 rx = self.send_and_expect(self.pg0, p4 * 15, self.pg1, 30)
501 inners.append(p[IP].payload)
502 reass_pkt = reassemble4(inners)
504 self.assert_packet_checksums_valid(p)
505 self.assertEqual(p[IP].ttl, 63)
507 def test_ipip_create(self):
508 """ipip create / delete interface test"""
509 rv = ipip_add_tunnel(self, "1.2.3.4", "2.3.4.5")
510 sw_if_index = rv.sw_if_index
511 self.vapi.ipip_del_tunnel(sw_if_index)
513 def test_ipip_vrf_create(self):
514 """ipip create / delete interface VRF test"""
516 t = VppIpTable(self, 20)
518 rv = ipip_add_tunnel(self, "1.2.3.4", "2.3.4.5", table_id=20)
519 sw_if_index = rv.sw_if_index
520 self.vapi.ipip_del_tunnel(sw_if_index)
522 def payload(self, len):
525 def test_mipip4(self):
526 """p2mp IPv4 tunnel Tests"""
528 for itf in self.pg_interfaces[:2]:
530 # one underlay nh for each overlay/tunnel peer
532 itf.generate_remote_hosts(4)
533 itf.configure_ipv4_neighbors()
536 # Create an p2mo IPIP tunnel.
538 # - assign an IP Addres
539 # - Add a route via the tunnel
541 ipip_if = VppIpIpTunInterface(
546 mode=(VppEnum.vl_api_tunnel_mode_t.TUNNEL_API_MODE_MP),
548 ipip_if.add_vpp_config()
551 ipip_if.generate_remote_hosts(4)
553 self.logger.info(self.vapi.cli("sh adj"))
554 self.logger.info(self.vapi.cli("sh ip fib"))
557 # ensure we don't match to the tunnel if the source address
560 # tx = self.create_tunnel_stream_4o4(self.pg0,
563 # self.pg0.local_ip4,
564 # self.pg0.remote_ip4)
565 # self.send_and_assert_no_replies(self.pg0, tx)
570 for ii in range(1, 4):
571 route_addr = "4.4.4.%d" % ii
574 # route traffic via the peer
576 route_via_tun = VppIpRoute(
580 [VppRoutePath(ipip_if._remote_hosts[ii].ip4, ipip_if.sw_if_index)],
582 route_via_tun.add_vpp_config()
585 # Add a TEIB entry resolves the peer
590 ipip_if._remote_hosts[ii].ip4,
591 itf._remote_hosts[ii].ip4,
593 teib.add_vpp_config()
595 self.vapi.cli("sh adj nbr ipip0 %s" % ipip_if._remote_hosts[ii].ip4)
599 # Send a packet stream that is routed into the tunnel
600 # - packets are IPIP encapped
603 IP(dst=route_addr, src="5.5.5.5")
604 / UDP(sport=1234, dport=1234)
608 (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / inner)
612 rxs = self.send_and_expect(self.pg0, tx_e, itf)
615 self.assertEqual(rx[IP].src, itf.local_ip4)
616 self.assertEqual(rx[IP].dst, itf._remote_hosts[ii].ip4)
620 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
621 / IP(src=itf._remote_hosts[ii].ip4, dst=itf.local_ip4)
622 / IP(src=self.pg0.local_ip4, dst=self.pg0.remote_ip4)
623 / UDP(sport=1234, dport=1234)
629 self.logger.info(self.vapi.cli("sh ipip tunnel-hash"))
630 rx = self.send_and_expect(self.pg0, tx_i, self.pg0)
633 # delete and re-add the TEIB
635 teib.remove_vpp_config()
636 self.send_and_assert_no_replies(self.pg0, tx_e)
637 self.send_and_assert_no_replies(self.pg0, tx_i)
639 teib.add_vpp_config()
640 rx = self.send_and_expect(self.pg0, tx_e, itf)
642 self.assertEqual(rx[IP].src, itf.local_ip4)
643 self.assertEqual(rx[IP].dst, itf._remote_hosts[ii].ip4)
644 rx = self.send_and_expect(self.pg0, tx_i, self.pg0)
647 # we can also send to the peer's address
650 IP(dst=teib.peer, src="5.5.5.5")
651 / UDP(sport=1234, dport=1234)
655 (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / inner)
659 rxs = self.send_and_expect(self.pg0, tx_e, itf)
662 # with all of the peers in place, swap the ip-table of
665 table = VppIpTable(self, 2)
666 table.add_vpp_config()
668 ipip_if.unconfig_ip4()
669 ipip_if.set_table_ip4(self.table.table_id)
673 # we should still be able to reach the peers from the new table
676 IP(dst=teib.peer, src="5.5.5.5")
677 / UDP(sport=1234, dport=1234)
681 (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / inner)
685 rxs = self.send_and_expect(self.pg2, tx_e, itf)
688 ipip_if.unconfig_ip4()
689 ipip_if.set_table_ip4(0)
692 class TestIPIP6(VppTestCase):
693 """IPIP6 Test Case"""
697 super(TestIPIP6, cls).setUpClass()
698 cls.create_pg_interfaces(range(2))
699 cls.interfaces = list(cls.pg_interfaces)
702 def tearDownClass(cls):
703 super(TestIPIP6, cls).tearDownClass()
706 super(TestIPIP6, self).setUp()
707 for i in self.interfaces:
717 if not self.vpp_dead:
718 self.destroy_tunnel()
719 for i in self.pg_interfaces:
723 super(TestIPIP6, self).tearDown()
725 def setup_tunnel(self):
727 rv = ipip_add_tunnel(self, self.pg0.local_ip6, self.pg1.remote_ip6)
729 sw_if_index = rv.sw_if_index
730 self.tunnel_if_index = sw_if_index
731 self.vapi.sw_interface_set_flags(sw_if_index, 1)
732 self.vapi.sw_interface_set_unnumbered(
733 sw_if_index=self.pg0.sw_if_index, unnumbered_sw_if_index=sw_if_index
736 # Add IPv4 and IPv6 routes via tunnel interface
737 ip4_via_tunnel = VppIpRoute(
743 "0.0.0.0", sw_if_index, proto=FibPathProto.FIB_PATH_NH_PROTO_IP4
747 ip4_via_tunnel.add_vpp_config()
749 ip6_via_tunnel = VppIpRoute(
753 [VppRoutePath("::", sw_if_index, proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)],
755 ip6_via_tunnel.add_vpp_config()
757 self.tunnel_ip6_via_tunnel = ip6_via_tunnel
758 self.tunnel_ip4_via_tunnel = ip4_via_tunnel
760 def destroy_tunnel(self):
762 self.tunnel_ip4_via_tunnel.remove_vpp_config()
763 self.tunnel_ip6_via_tunnel.remove_vpp_config()
765 rv = self.vapi.ipip_del_tunnel(sw_if_index=self.tunnel_if_index)
767 def validate(self, rx, expected):
768 self.assertEqual(rx, expected.__class__(expected))
770 def generate_ip6_frags(self, payload_length, fragment_size):
771 p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
772 p_payload = UDP(sport=1234, dport=1234) / self.payload(payload_length)
773 p_ip6 = IPv6(src="1::1", dst=self.pg0.remote_ip6)
776 / IPv6(src=self.pg1.remote_ip6, dst=self.pg0.local_ip6)
777 / IPv6ExtHdrFragment()
781 frags = fragment6(outer_ip6, fragment_size)
782 p6_reply = p_ip6 / p_payload
784 return frags, p6_reply
786 def generate_ip6_hairpin_frags(self, payload_length, fragment_size):
787 p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
788 p_payload = UDP(sport=1234, dport=1234) / self.payload(payload_length)
789 p_ip6 = IPv6(src="1::1", dst="dead::1")
792 / IPv6(src=self.pg1.remote_ip6, dst=self.pg0.local_ip6)
793 / IPv6ExtHdrFragment()
797 frags = fragment6(outer_ip6, fragment_size)
800 IPv6(src=self.pg0.local_ip6, dst=self.pg1.remote_ip6, hlim=63)
805 return frags, p6_reply
807 def test_encap(self):
808 """ip{v4,v6} over ip6 test encap"""
809 p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
810 p_ip6 = IPv6(src="1::1", dst="DEAD::1", tc=42, nh="UDP")
811 p_ip4 = IP(src="1.2.3.4", dst="130.67.0.1", tos=42)
812 p_payload = UDP(sport=1234, dport=1234)
815 # IPv6 in to IPv6 tunnel
816 p6 = p_ether / p_ip6 / p_payload
818 IPv6(src=self.pg0.local_ip6, dst=self.pg1.remote_ip6, hlim=64)
822 p6_reply[1].hlim -= 1
823 rx = self.send_and_expect(self.pg0, p6 * 11, self.pg1)
825 self.validate(p[1], p6_reply)
827 # IPv4 in to IPv6 tunnel
828 p4 = p_ether / p_ip4 / p_payload
830 IPv6(src=self.pg0.local_ip6, dst=self.pg1.remote_ip6, hlim=64)
835 rx = self.send_and_expect(self.pg0, p4 * 11, self.pg1)
837 self.validate(p[1], p4_reply)
839 def test_decap(self):
840 """ip{v4,v6} over ip6 test decap"""
842 p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
843 p_ip6 = IPv6(src="1::1", dst="DEAD::1", tc=42, nh="UDP")
844 p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
845 p_payload = UDP(sport=1234, dport=1234)
848 # IPv6 tunnel to IPv4
852 / IPv6(src=self.pg1.remote_ip6, dst=self.pg0.local_ip6)
856 p4_reply = p_ip4 / p_payload
858 rx = self.send_and_expect(self.pg1, p4 * 11, self.pg0)
860 self.validate(p[1], p4_reply)
862 # IPv6 tunnel to IPv6
863 p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
866 / IPv6(src=self.pg1.remote_ip6, dst=self.pg0.local_ip6)
870 p6_reply = p_ip6 / p_payload
872 rx = self.send_and_expect(self.pg1, p6 * 11, self.pg0)
874 self.validate(p[1], p6_reply)
876 def verify_ip4ip6_encaps(self, a, p_ip4s, p_ip6_encaps):
877 for i, p_ip4 in enumerate(p_ip4s):
879 p4 = self.p_ether / p_ip4 / self.p_payload
882 p6_reply = p_ip6_encaps[i] / p_ip4_inner / self.p_payload
883 rx = self.send_and_expect(self.pg0, p4 * N_PACKETS, self.pg1)
885 self.validate(p[1], p6_reply)
886 self.assert_packet_checksums_valid(p)
888 def verify_ip6ip6_encaps(self, a, p_ip6s, p_ip6_encaps):
889 for i, p_ip6 in enumerate(p_ip6s):
891 p6 = self.p_ether / p_ip6 / self.p_payload
893 p_inner_ip6.hlim -= 1
894 p6_reply = p_ip6_encaps[i] / p_inner_ip6 / self.p_payload
895 rx = self.send_and_expect(self.pg0, p6 * N_PACKETS, self.pg1)
897 self.validate(p[1], p6_reply)
898 self.assert_packet_checksums_valid(p)
900 def test_ipip6(self):
901 """ip{v4,v6} over ip6 test"""
904 self.destroy_tunnel()
906 self.pg1.generate_remote_hosts(5)
907 self.pg1.configure_ipv6_neighbors()
908 e = VppEnum.vl_api_tunnel_encap_decap_flags_t
909 d = VppEnum.vl_api_ip_dscp_t
910 self.p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
911 self.p_payload = UDP(sport=1234, dport=1234) / Raw(b"X" * 100)
913 # create a TOS byte by shifting a DSCP code point 2 bits. those 2 bits
915 dscp = d.IP_API_DSCP_AF31 << 2
917 dscp_ecn = d.IP_API_DSCP_AF31 << 2 | ecn
919 # IPv4 transport that copies the DCSP from the payload
920 tun_dscp = VppIpIpTunInterface(
924 self.pg1.remote_hosts[0].ip6,
925 flags=e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP,
927 tun_dscp.add_vpp_config()
928 # IPv4 transport that copies the DCSP and ECN from the payload
929 tun_dscp_ecn = VppIpIpTunInterface(
933 self.pg1.remote_hosts[1].ip6,
935 e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP
936 | e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN
939 tun_dscp_ecn.add_vpp_config()
940 # IPv4 transport that copies the ECN from the payload and sets the
941 # DF bit on encap. copies the ECN on decap
942 tun_ecn = VppIpIpTunInterface(
946 self.pg1.remote_hosts[2].ip6,
948 e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN
949 | e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF
950 | e.TUNNEL_API_ENCAP_DECAP_FLAG_DECAP_COPY_ECN
953 tun_ecn.add_vpp_config()
954 # IPv4 transport that sets a fixed DSCP in the encap and copies
956 tun = VppIpIpTunInterface(
960 self.pg1.remote_hosts[3].ip6,
961 dscp=d.IP_API_DSCP_AF11,
962 flags=e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF,
966 # array of all the tunnels
967 tuns = [tun_dscp, tun_dscp_ecn, tun_ecn, tun]
969 # addresses for prefixes routed via each tunnel
970 a4s = ["" for i in range(len(tuns))]
971 a6s = ["" for i in range(len(tuns))]
973 # IP headers for inner packets with each combination of DSCp/ECN tested
975 IPv6(src="1::1", dst="DEAD::1", nh="UDP", tc=dscp),
976 IPv6(src="1::1", dst="DEAD::1", nh="UDP", tc=dscp_ecn),
977 IPv6(src="1::1", dst="DEAD::1", nh="UDP", tc=ecn),
978 IPv6(src="1::1", dst="DEAD::1", nh="UDP", tc=0xFF),
981 IP(src="1.2.3.4", dst="130.67.0.1", tos=dscp, flags="DF"),
982 IP(src="1.2.3.4", dst="130.67.0.1", tos=dscp_ecn),
983 IP(src="1.2.3.4", dst="130.67.0.1", tos=ecn),
984 IP(src="1.2.3.4", dst="130.67.0.1", tos=0xFF),
987 # Configure each tunnel
988 for i, t in enumerate(tuns):
989 # Set interface up and enable IP on it
990 self.vapi.sw_interface_set_flags(t.sw_if_index, 1)
991 self.vapi.sw_interface_set_unnumbered(
992 sw_if_index=self.pg0.sw_if_index, unnumbered_sw_if_index=t.sw_if_index
995 # prefix for route / destination address for packets
996 a4s[i] = "130.67.%d.0" % i
997 a6s[i] = "dead:%d::" % i
999 # Add IPv4 and IPv6 routes via tunnel interface
1000 ip4_via_tunnel = VppIpRoute(
1008 proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
1012 ip4_via_tunnel.add_vpp_config()
1014 ip6_via_tunnel = VppIpRoute(
1020 "::", t.sw_if_index, proto=FibPathProto.FIB_PATH_NH_PROTO_IP6
1024 ip6_via_tunnel.add_vpp_config()
1030 # tun_dscp copies only the dscp
1031 # expected TC values are thus only the DCSP value is present from the
1033 exp_tcs = [dscp, dscp, 0, 0xFC]
1035 IPv6(src=self.pg0.local_ip6, dst=tun_dscp.dst, tc=tc) for tc in exp_tcs
1038 # IPv4 in to IPv4 tunnel
1039 self.verify_ip4ip6_encaps(a4s[0], p_ip4s, p_ip6_encaps)
1040 # IPv6 in to IPv4 tunnel
1041 self.verify_ip6ip6_encaps(a6s[0], p_ip6s, p_ip6_encaps)
1043 # tun_dscp_ecn copies the dscp and the ecn
1044 exp_tcs = [dscp, dscp_ecn, ecn, 0xFF]
1046 IPv6(src=self.pg0.local_ip6, dst=tun_dscp_ecn.dst, tc=tc) for tc in exp_tcs
1049 self.verify_ip4ip6_encaps(a4s[1], p_ip4s, p_ip6_encaps)
1050 self.verify_ip6ip6_encaps(a6s[1], p_ip6s, p_ip6_encaps)
1052 # tun_ecn copies only the ecn and always sets DF
1053 exp_tcs = [0, ecn, ecn, ecn]
1055 IPv6(src=self.pg0.local_ip6, dst=tun_ecn.dst, tc=tc) for tc in exp_tcs
1058 self.verify_ip4ip6_encaps(a4s[2], p_ip4s, p_ip6_encaps)
1059 self.verify_ip6ip6_encaps(a6s[2], p_ip6s, p_ip6_encaps)
1061 # tun sets a fixed dscp
1062 fixed_dscp = tun.dscp << 2
1064 IPv6(src=self.pg0.local_ip6, dst=tun.dst, tc=fixed_dscp)
1065 for i in range(len(p_ip4s))
1068 self.verify_ip4ip6_encaps(a4s[3], p_ip4s, p_ip6_encaps)
1069 self.verify_ip6ip6_encaps(a6s[3], p_ip6s, p_ip6_encaps)
1074 n_packets_decapped = self.statistics.get_err_counter(
1075 "/err/ipip6-input/packets decapsulated"
1078 self.p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
1080 # IPv6 tunnel to IPv4
1081 tcs = [0, dscp, dscp_ecn, ecn]
1083 # one overlay packet and all combinations of its encap
1084 p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
1085 p_ip6_encaps = [IPv6(src=tun.dst, dst=self.pg0.local_ip6, tc=tc) for tc in tcs]
1087 # for each encap tun will produce the same inner packet because it does
1088 # not copy up fields from the payload
1089 for p_ip6_encap in p_ip6_encaps:
1090 p6 = self.p_ether / p_ip6_encap / p_ip4 / self.p_payload
1091 p4_reply = p_ip4 / self.p_payload
1093 rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0)
1094 n_packets_decapped += N_PACKETS
1096 self.validate(p[1], p4_reply)
1097 self.assert_packet_checksums_valid(p)
1099 err = self.statistics.get_err_counter("/err/ipip6-input/packets decapsulated")
1100 self.assertEqual(err, n_packets_decapped)
1102 # tun_ecn copies the ECN bits from the encap to the inner
1104 IPv6(src=tun_ecn.dst, dst=self.pg0.local_ip6, tc=tc) for tc in tcs
1106 p_ip4_replys = [p_ip4.copy() for i in range(len(p_ip6_encaps))]
1107 p_ip4_replys[2].tos = ecn
1108 p_ip4_replys[3].tos = ecn
1109 for i, p_ip6_encap in enumerate(p_ip6_encaps):
1110 p6 = self.p_ether / p_ip6_encap / p_ip4 / self.p_payload
1111 p4_reply = p_ip4_replys[i] / self.p_payload
1113 rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0)
1114 n_packets_decapped += N_PACKETS
1116 self.validate(p[1], p4_reply)
1117 self.assert_packet_checksums_valid(p)
1119 err = self.statistics.get_err_counter("/err/ipip6-input/packets decapsulated")
1120 self.assertEqual(err, n_packets_decapped)
1122 # IPv6 tunnel to IPv6
1123 # for each encap tun will produce the same inner packet because it does
1124 # not copy up fields from the payload
1125 p_ip6_encaps = [IPv6(src=tun.dst, dst=self.pg0.local_ip6, tc=tc) for tc in tcs]
1126 p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
1127 for p_ip6_encap in p_ip6_encaps:
1128 p6 = self.p_ether / p_ip6_encap / p_ip6 / self.p_payload
1129 p6_reply = p_ip6 / self.p_payload
1131 rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0)
1132 n_packets_decapped += N_PACKETS
1134 self.validate(p[1], p6_reply)
1135 self.assert_packet_checksums_valid(p)
1137 err = self.statistics.get_err_counter("/err/ipip6-input/packets decapsulated")
1138 self.assertEqual(err, n_packets_decapped)
1140 # IPv6 tunnel to IPv6
1141 # tun_ecn copies the ECN bits from the encap to the inner
1143 IPv6(src=tun_ecn.dst, dst=self.pg0.local_ip6, tc=tc) for tc in tcs
1145 p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
1146 p_ip6_replys = [p_ip6.copy() for i in range(len(p_ip6_encaps))]
1147 p_ip6_replys[2].tc = ecn
1148 p_ip6_replys[3].tc = ecn
1149 for i, p_ip6_encap in enumerate(p_ip6_encaps):
1150 p6 = self.p_ether / p_ip6_encap / p_ip6 / self.p_payload
1151 p6_reply = p_ip6_replys[i] / self.p_payload
1153 rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0)
1154 n_packets_decapped += N_PACKETS
1156 self.validate(p[1], p6_reply)
1157 self.assert_packet_checksums_valid(p)
1159 err = self.statistics.get_err_counter("/err/ipip6-input/packets decapsulated")
1160 self.assertEqual(err, n_packets_decapped)
1162 def test_frag(self):
1163 """ip{v4,v6} over ip6 test frag"""
1165 p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
1166 p_ip6 = IPv6(src="1::1", dst="DEAD::1", tc=42, nh="UDP")
1167 p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
1168 p_payload = UDP(sport=1234, dport=1234)
1171 # Fragmentation / Reassembly and Re-fragmentation
1173 rv = self.vapi.ip_reassembly_enable_disable(
1174 sw_if_index=self.pg1.sw_if_index, enable_ip6=1
1177 self.vapi.ip_reassembly_set(
1179 max_reassemblies=1000,
1180 max_reassembly_length=1000,
1181 expire_walk_interval_ms=10000,
1185 # Send lots of fragments, verify reassembled packet
1186 before_cnt = self.statistics.get_err_counter(
1187 "/err/ipip6-input/packets decapsulated"
1189 frags, p6_reply = self.generate_ip6_frags(3131, 1400)
1191 for i in range(0, 1000):
1193 self.pg1.add_stream(f)
1194 self.pg_enable_capture()
1196 rx = self.pg0.get_capture(1000)
1199 self.validate(p[1], p6_reply)
1201 cnt = self.statistics.get_err_counter("/err/ipip6-input/packets decapsulated")
1202 self.assertEqual(cnt, before_cnt + 1000)
1206 # TODO: Check out why reassembly of atomic fragments don't work
1207 for i in range(10, 90):
1208 frags, p6_reply = self.generate_ip6_frags(i * 100, 1000)
1211 self.pg_enable_capture()
1212 self.pg1.add_stream(f)
1214 rx = self.pg0.get_capture(80)
1217 self.validate(p[1], r[i])
1220 # Simple fragmentation
1221 p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1222 self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1280, 0, 0, 0])
1224 # IPv6 in to IPv6 tunnel
1225 p_payload = UDP(sport=1234, dport=1234) / self.payload(1300)
1227 p6 = p_ether / p_ip6 / p_payload
1229 IPv6(src=self.pg0.local_ip6, dst=self.pg1.remote_ip6, hlim=63)
1233 p6_reply[1].hlim -= 1
1234 self.pg_enable_capture()
1235 self.pg0.add_stream(p6)
1237 rx = self.pg1.get_capture(2)
1239 # Scapy defragment doesn't deal well with multiple layers
1240 # of same type / Ethernet header first
1241 f = [p[1] for p in rx]
1242 reass_pkt = defragment6(f)
1243 self.validate(reass_pkt, p6_reply)
1245 # Now try with re-fragmentation
1247 # Send large fragments to tunnel head-end, for the tunnel head end
1248 # to reassemble and then refragment out the tunnel again.
1251 self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1280, 0, 0, 0])
1252 frags, p6_reply = self.generate_ip6_hairpin_frags(8000, 1200)
1253 self.pg_enable_capture()
1254 self.pg1.add_stream(frags)
1256 rx = self.pg1.get_capture(7)
1257 f = [p[1] for p in rx]
1258 reass_pkt = defragment6(f)
1260 self.validate(reass_pkt, p6_reply)
1262 def test_ip6_mpls_frag(self):
1263 """Test fragmenting IPv6 over MPLS"""
1265 # IPv6 packets must be locally generated to be fragmented
1266 # the use of tunnel encaps
1267 tun_dst = VppIpRoute(
1273 self.pg1.remote_ip6, self.pg1.sw_if_index, labels=[VppMplsLabel(32)]
1278 tun = VppIpIpTunInterface(
1279 self, self.pg0, self.pg0.local_ip6, "1000::1"
1286 self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [2000, 0, 0, 0])
1289 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1290 / IPv6(src=self.pg0.remote_ip6, dst=tun.remote_ip6)
1291 / UDP(sport=1234, dport=5678)
1292 / Raw(b"0xa" * 2000)
1295 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1296 / IPv6(src=self.pg0.remote_ip6, dst=tun.remote_ip6)
1297 / UDP(sport=1234, dport=5678)
1298 / Raw(b"0xa" * 1000)
1301 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1302 / IPv6(src=self.pg0.remote_ip6, dst=tun.remote_ip6)
1303 / UDP(sport=1234, dport=5678)
1307 # this is now the interface MTU frags
1308 rxs = self.send_and_expect(self.pg0, [p_6k], self.pg1, n_rx=4)
1309 self.assertEqual(rxs[0][UDP].dport, 5678)
1311 self.assertEqual(rx[MPLS].label, 32)
1312 self.assertEqual(rx[IPv6].dst, "1000::1")
1313 self.assertEqual(rx[IPv6].dst, "1000::1")
1314 self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=2)
1315 self.send_and_expect(self.pg0, [p_1k], self.pg1)
1317 def test_ipip_create(self):
1318 """ipip create / delete interface test"""
1319 rv = ipip_add_tunnel(self, "1.2.3.4", "2.3.4.5")
1320 sw_if_index = rv.sw_if_index
1321 self.vapi.ipip_del_tunnel(sw_if_index)
1323 def test_ipip_vrf_create(self):
1324 """ipip create / delete interface VRF test"""
1326 t = VppIpTable(self, 20)
1328 rv = ipip_add_tunnel(self, "1.2.3.4", "2.3.4.5", table_id=20)
1329 sw_if_index = rv.sw_if_index
1330 self.vapi.ipip_del_tunnel(sw_if_index)
1332 def payload(self, len):
1336 class TestIPIPMPLS(VppTestCase):
1337 """MPLS Test Case"""
1340 def setUpClass(cls):
1341 super(TestIPIPMPLS, cls).setUpClass()
1342 cls.create_pg_interfaces(range(2))
1343 cls.interfaces = list(cls.pg_interfaces)
1346 def tearDownClass(cls):
1347 super(TestIPIPMPLS, cls).tearDownClass()
1350 super(TestIPIPMPLS, self).setUp()
1351 for i in self.interfaces:
1360 super(TestIPIPMPLS, self).tearDown()
1362 for i in self.pg_interfaces:
1367 def test_mpls(self):
1368 """MPLS over ip{6,4} test"""
1370 tbl = VppMplsTable(self, 0)
1371 tbl.add_vpp_config()
1373 self.p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1374 self.p_payload = UDP(sport=1234, dport=1234) / Raw(b"X" * 100)
1378 tun4 = VppIpIpTunInterface(
1379 self, self.pg1, self.pg1.local_ip4, self.pg1.remote_ip4
1386 tun6 = VppIpIpTunInterface(
1387 self, self.pg1, self.pg1.local_ip6, self.pg1.remote_ip6
1393 # ip routes into the tunnels with output labels
1400 tun4.remote_ip4, tun4.sw_if_index, labels=[VppMplsLabel(44)]
1410 tun6.remote_ip6, tun6.sw_if_index, labels=[VppMplsLabel(66)]
1415 # deag MPLS routes from the tunnel
1417 self, 44, 1, [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index)]
1423 [VppRoutePath(self.pg0.remote_ip6, self.pg0.sw_if_index)],
1424 eos_proto=f.FIB_PATH_NH_PROTO_IP6,
1430 p4 = self.p_ether / IP(src="2.2.2.2", dst="1.1.1.1") / self.p_payload
1432 rxs = self.send_and_expect(self.pg0, p4 * N_PACKETS, self.pg1)
1435 self.assertEqual(rx[IP].src, self.pg1.local_ip4)
1436 self.assertEqual(rx[IP].dst, self.pg1.remote_ip4)
1437 self.assertEqual(rx[MPLS].label, 44)
1438 inner = rx[MPLS].payload
1439 self.assertEqual(inner.src, "2.2.2.2")
1440 self.assertEqual(inner.dst, "1.1.1.1")
1442 p6 = self.p_ether / IPv6(src="2::2", dst="1::1") / self.p_payload
1444 rxs = self.send_and_expect(self.pg0, p6 * N_PACKETS, self.pg1)
1447 self.assertEqual(rx[IPv6].src, self.pg1.local_ip6)
1448 self.assertEqual(rx[IPv6].dst, self.pg1.remote_ip6)
1449 self.assertEqual(rx[MPLS].label, 66)
1450 inner = rx[MPLS].payload
1451 self.assertEqual(inner.src, "2::2")
1452 self.assertEqual(inner.dst, "1::1")
1459 / IP(src=self.pg1.remote_ip4, dst=self.pg1.local_ip4)
1460 / MPLS(label=44, ttl=4)
1461 / IP(src="1.1.1.1", dst="2.2.2.2")
1465 rxs = self.send_and_expect(self.pg1, p4 * N_PACKETS, self.pg0)
1468 self.assertEqual(rx[IP].src, "1.1.1.1")
1469 self.assertEqual(rx[IP].dst, "2.2.2.2")
1473 / IPv6(src=self.pg1.remote_ip6, dst=self.pg1.local_ip6)
1474 / MPLS(label=66, ttl=4)
1475 / IPv6(src="1::1", dst="2::2")
1479 rxs = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0)
1482 self.assertEqual(rx[IPv6].src, "1::1")
1483 self.assertEqual(rx[IPv6].dst, "2::2")
1489 if __name__ == "__main__":
1490 unittest.main(testRunner=VppTestRunner)