5 from socket import AF_INET, AF_INET6, inet_pton
7 from framework import tag_fixme_vpp_workers
8 from framework import VppTestCase, VppTestRunner
9 from vpp_neighbor import VppNeighbor, find_nbr
10 from vpp_ip_route import (
17 VppIpInterfaceAddress,
19 from vpp_papi import VppEnum
20 from vpp_ip import VppIpPuntRedirect
23 from scapy.packet import Raw
24 from scapy.layers.l2 import Ether, ARP, Dot1Q
25 from scapy.layers.inet import IP, UDP, TCP
26 from scapy.layers.inet6 import IPv6
27 from scapy.contrib.mpls import MPLS
28 from scapy.layers.inet6 import IPv6
33 # not exported by scapy, so redefined here
34 arp_opts = {"who-has": 1, "is-at": 2}
37 class ARPTestCase(VppTestCase):
42 super(ARPTestCase, cls).setUpClass()
45 def tearDownClass(cls):
46 super(ARPTestCase, cls).tearDownClass()
49 super(ARPTestCase, self).setUp()
51 # create 3 pg interfaces
52 self.create_pg_interfaces(range(4))
54 # pg0 configured with ip4 and 6 addresses used for input
55 # pg1 configured with ip4 and 6 addresses used for output
56 # pg2 is unnumbered to pg0
57 for i in self.pg_interfaces:
62 self.pg0.resolve_arp()
67 # pg3 in a different VRF
68 self.tbl = VppIpTable(self, 1)
69 self.tbl.add_vpp_config()
71 self.pg3.set_table_ip4(1)
75 self.pg0.unconfig_ip4()
76 self.pg0.unconfig_ip6()
78 self.pg1.unconfig_ip4()
79 self.pg1.unconfig_ip6()
81 self.pg3.unconfig_ip4()
82 self.pg3.set_table_ip4(0)
84 for i in self.pg_interfaces:
87 super(ARPTestCase, self).tearDown()
89 def verify_arp_req(self, rx, smac, sip, dip):
91 self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
92 self.assertEqual(ether.src, smac)
93 self.assertEqual(ether.type, 0x0806)
96 self.assertEqual(arp.hwtype, 1)
97 self.assertEqual(arp.ptype, 0x800)
98 self.assertEqual(arp.hwlen, 6)
99 self.assertEqual(arp.plen, 4)
100 self.assertEqual(arp.op, arp_opts["who-has"])
101 self.assertEqual(arp.hwsrc, smac)
102 self.assertEqual(arp.hwdst, "00:00:00:00:00:00")
103 self.assertEqual(arp.psrc, sip)
104 self.assertEqual(arp.pdst, dip)
106 def verify_arp_resp(self, rx, smac, dmac, sip, dip):
108 self.assertEqual(ether.dst, dmac)
109 self.assertEqual(ether.src, smac)
110 self.assertEqual(ether.type, 0x0806)
113 self.assertEqual(arp.hwtype, 1)
114 self.assertEqual(arp.ptype, 0x800)
115 self.assertEqual(arp.hwlen, 6)
116 self.assertEqual(arp.plen, 4)
117 self.assertEqual(arp.op, arp_opts["is-at"])
118 self.assertEqual(arp.hwsrc, smac)
119 self.assertEqual(arp.hwdst, dmac)
120 self.assertEqual(arp.psrc, sip)
121 self.assertEqual(arp.pdst, dip)
123 def verify_arp_vrrp_resp(self, rx, smac, dmac, sip, dip):
125 self.assertEqual(ether.dst, dmac)
126 self.assertEqual(ether.src, smac)
129 self.assertEqual(arp.hwtype, 1)
130 self.assertEqual(arp.ptype, 0x800)
131 self.assertEqual(arp.hwlen, 6)
132 self.assertEqual(arp.plen, 4)
133 self.assertEqual(arp.op, arp_opts["is-at"])
134 self.assertNotEqual(arp.hwsrc, smac)
135 self.assertTrue("00:00:5e:00:01" in arp.hwsrc or "00:00:5E:00:01" in arp.hwsrc)
136 self.assertEqual(arp.hwdst, dmac)
137 self.assertEqual(arp.psrc, sip)
138 self.assertEqual(arp.pdst, dip)
140 def verify_ip(self, rx, smac, dmac, sip, dip):
142 self.assertEqual(ether.dst, dmac)
143 self.assertEqual(ether.src, smac)
144 self.assertEqual(ether.type, 0x0800)
147 self.assertEqual(ip.src, sip)
148 self.assertEqual(ip.dst, dip)
150 def verify_ip_o_mpls(self, rx, smac, dmac, label, sip, dip):
152 self.assertEqual(ether.dst, dmac)
153 self.assertEqual(ether.src, smac)
154 self.assertEqual(ether.type, 0x8847)
157 self.assertTrue(mpls.label, label)
160 self.assertEqual(ip.src, sip)
161 self.assertEqual(ip.dst, dip)
163 def get_arp_rx_requests(self, itf):
164 """Get ARP RX request stats for and interface"""
165 return self.statistics["/net/arp/rx/requests"][:, itf.sw_if_index].sum()
167 def get_arp_tx_requests(self, itf):
168 """Get ARP TX request stats for and interface"""
169 return self.statistics["/net/arp/tx/requests"][:, itf.sw_if_index].sum()
171 def get_arp_rx_replies(self, itf):
172 """Get ARP RX replies stats for and interface"""
173 return self.statistics["/net/arp/rx/replies"][:, itf.sw_if_index].sum()
175 def get_arp_tx_replies(self, itf):
176 """Get ARP TX replies stats for and interface"""
177 return self.statistics["/net/arp/tx/replies"][:, itf.sw_if_index].sum()
179 def get_arp_rx_garp(self, itf):
180 """Get ARP RX grat stats for and interface"""
181 return self.statistics["/net/arp/rx/gratuitous"][:, itf.sw_if_index].sum()
183 def get_arp_tx_garp(self, itf):
184 """Get ARP RX grat stats for and interface"""
185 return self.statistics["/net/arp/tx/gratuitous"][:, itf.sw_if_index].sum()
191 # Generate some hosts on the LAN
193 self.pg1.generate_remote_hosts(11)
197 # - all neighbour events
198 # - all neighbor events on pg1
199 # - neighbor events for host[1] on pg1
201 self.vapi.want_ip_neighbor_events(enable=1, pid=os.getpid())
202 self.vapi.want_ip_neighbor_events(
203 enable=1, pid=os.getpid(), sw_if_index=self.pg1.sw_if_index
205 self.vapi.want_ip_neighbor_events(
208 sw_if_index=self.pg1.sw_if_index,
209 ip=self.pg1.remote_hosts[1].ip4,
212 self.logger.info(self.vapi.cli("sh ip neighbor-watcher"))
215 # Send IP traffic to one of these unresolved hosts.
216 # expect the generation of an ARP request
219 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
220 / IP(src=self.pg0.remote_ip4, dst=self.pg1._remote_hosts[1].ip4)
221 / UDP(sport=1234, dport=1234)
225 self.pg0.add_stream(p)
226 self.pg_enable_capture(self.pg_interfaces)
229 rx = self.pg1.get_capture(1)
232 rx[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1._remote_hosts[1].ip4
235 self.logger.info(self.vapi.cli("sh ip neighbor-stats"))
236 self.logger.info(self.vapi.cli("sh ip neighbor-stats pg1"))
237 self.assert_equal(self.get_arp_tx_requests(self.pg1), 1)
240 # And a dynamic ARP entry for host 1
242 dyn_arp = VppNeighbor(
244 self.pg1.sw_if_index,
245 self.pg1.remote_hosts[1].mac,
246 self.pg1.remote_hosts[1].ip4,
248 dyn_arp.add_vpp_config()
249 self.assertTrue(dyn_arp.query_vpp_config())
251 self.logger.info(self.vapi.cli("show ip neighbor-watcher"))
253 # this matches all of the listnerers
254 es = [self.vapi.wait_for_event(1, "ip_neighbor_event") for i in range(3)]
256 self.assertEqual(str(e.neighbor.ip_address), self.pg1.remote_hosts[1].ip4)
259 # now we expect IP traffic forwarded
262 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
263 / IP(src=self.pg0.remote_ip4, dst=self.pg1._remote_hosts[1].ip4)
264 / UDP(sport=1234, dport=1234)
268 self.pg0.add_stream(dyn_p)
269 self.pg_enable_capture(self.pg_interfaces)
272 rx = self.pg1.get_capture(1)
277 self.pg1.remote_hosts[1].mac,
279 self.pg1._remote_hosts[1].ip4,
283 # And a Static ARP entry for host 2
285 static_arp = VppNeighbor(
287 self.pg1.sw_if_index,
288 self.pg1.remote_hosts[2].mac,
289 self.pg1.remote_hosts[2].ip4,
292 static_arp.add_vpp_config()
293 es = [self.vapi.wait_for_event(1, "ip_neighbor_event") for i in range(2)]
295 self.assertEqual(str(e.neighbor.ip_address), self.pg1.remote_hosts[2].ip4)
298 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
299 / IP(src=self.pg0.remote_ip4, dst=self.pg1._remote_hosts[2].ip4)
300 / UDP(sport=1234, dport=1234)
304 self.pg0.add_stream(static_p)
305 self.pg_enable_capture(self.pg_interfaces)
308 rx = self.pg1.get_capture(1)
313 self.pg1.remote_hosts[2].mac,
315 self.pg1._remote_hosts[2].ip4,
319 # remove all the listeners
321 self.vapi.want_ip_neighbor_events(enable=0, pid=os.getpid())
322 self.vapi.want_ip_neighbor_events(
323 enable=0, pid=os.getpid(), sw_if_index=self.pg1.sw_if_index
325 self.vapi.want_ip_neighbor_events(
328 sw_if_index=self.pg1.sw_if_index,
329 ip=self.pg1.remote_hosts[1].ip4,
333 # flap the link. dynamic ARPs get flush, statics don't
335 self.pg1.admin_down()
338 self.pg0.add_stream(static_p)
339 self.pg_enable_capture(self.pg_interfaces)
341 rx = self.pg1.get_capture(1)
346 self.pg1.remote_hosts[2].mac,
348 self.pg1._remote_hosts[2].ip4,
351 self.pg0.add_stream(dyn_p)
352 self.pg_enable_capture(self.pg_interfaces)
355 rx = self.pg1.get_capture(1)
357 rx[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1._remote_hosts[1].ip4
359 self.assert_equal(self.get_arp_tx_requests(self.pg1), 2)
361 self.assertFalse(dyn_arp.query_vpp_config())
362 self.assertTrue(static_arp.query_vpp_config())
364 # Send an ARP request from one of the so-far unlearned remote hosts
366 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1._remote_hosts[3].mac) / ARP(
368 hwsrc=self.pg1._remote_hosts[3].mac,
369 pdst=self.pg1.local_ip4,
370 psrc=self.pg1._remote_hosts[3].ip4,
373 self.pg1.add_stream(p)
374 self.pg_enable_capture(self.pg_interfaces)
377 rx = self.pg1.get_capture(1)
378 self.verify_arp_resp(
381 self.pg1._remote_hosts[3].mac,
383 self.pg1._remote_hosts[3].ip4,
385 self.logger.info(self.vapi.cli("sh ip neighbor-stats pg1"))
386 self.assert_equal(self.get_arp_rx_requests(self.pg1), 1)
387 self.assert_equal(self.get_arp_tx_replies(self.pg1), 1)
390 # VPP should have learned the mapping for the remote host
393 find_nbr(self, self.pg1.sw_if_index, self.pg1._remote_hosts[3].ip4)
396 # Fire in an ARP request before the interface becomes IP enabled
398 self.pg2.generate_remote_hosts(4)
400 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(
402 hwsrc=self.pg2.remote_mac,
403 pdst=self.pg1.local_ip4,
404 psrc=self.pg2.remote_hosts[3].ip4,
407 Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac)
411 hwsrc=self.pg2.remote_mac,
412 pdst=self.pg1.local_ip4,
413 psrc=self.pg2.remote_hosts[3].ip4,
416 self.send_and_assert_no_replies(self.pg2, p, "interface not IP enabled")
419 # Make pg2 un-numbered to pg1
421 self.pg2.set_unnumbered(self.pg1.sw_if_index)
424 # test the unnumbered dump both by all interfaces and just the enabled
427 unnum = self.vapi.ip_unnumbered_dump()
428 self.assertTrue(len(unnum))
429 self.assertEqual(unnum[0].ip_sw_if_index, self.pg1.sw_if_index)
430 self.assertEqual(unnum[0].sw_if_index, self.pg2.sw_if_index)
431 unnum = self.vapi.ip_unnumbered_dump(self.pg2.sw_if_index)
432 self.assertTrue(len(unnum))
433 self.assertEqual(unnum[0].ip_sw_if_index, self.pg1.sw_if_index)
434 self.assertEqual(unnum[0].sw_if_index, self.pg2.sw_if_index)
437 # We should respond to ARP requests for the unnumbered to address
438 # once an attached route to the source is known
440 self.send_and_assert_no_replies(
441 self.pg2, p, "ARP req for unnumbered address - no source"
444 attached_host = VppIpRoute(
446 self.pg2.remote_hosts[3].ip4,
448 [VppRoutePath("0.0.0.0", self.pg2.sw_if_index)],
450 attached_host.add_vpp_config()
452 self.pg2.add_stream(p)
453 self.pg_enable_capture(self.pg_interfaces)
456 rx = self.pg2.get_capture(1)
457 self.verify_arp_resp(
462 self.pg2.remote_hosts[3].ip4,
465 self.pg2.add_stream(pt)
466 self.pg_enable_capture(self.pg_interfaces)
469 rx = self.pg2.get_capture(1)
470 self.verify_arp_resp(
475 self.pg2.remote_hosts[3].ip4,
479 # A neighbor entry that has no associated FIB-entry
481 arp_no_fib = VppNeighbor(
483 self.pg1.sw_if_index,
484 self.pg1.remote_hosts[4].mac,
485 self.pg1.remote_hosts[4].ip4,
488 arp_no_fib.add_vpp_config()
491 # check we have the neighbor, but no route
494 find_nbr(self, self.pg1.sw_if_index, self.pg1._remote_hosts[4].ip4)
496 self.assertFalse(find_route(self, self.pg1._remote_hosts[4].ip4, 32))
498 # pg2 is unnumbered to pg1, so we can form adjacencies out of pg2
499 # from within pg1's subnet
501 arp_unnum = VppNeighbor(
503 self.pg2.sw_if_index,
504 self.pg1.remote_hosts[5].mac,
505 self.pg1.remote_hosts[5].ip4,
507 arp_unnum.add_vpp_config()
510 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
511 / IP(src=self.pg0.remote_ip4, dst=self.pg1._remote_hosts[5].ip4)
512 / UDP(sport=1234, dport=1234)
516 self.pg0.add_stream(p)
517 self.pg_enable_capture(self.pg_interfaces)
520 rx = self.pg2.get_capture(1)
525 self.pg1.remote_hosts[5].mac,
527 self.pg1._remote_hosts[5].ip4,
531 # ARP requests from hosts in pg1's subnet sent on pg2 are replied to
532 # with the unnumbered interface's address as the source
534 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(
536 hwsrc=self.pg2.remote_mac,
537 pdst=self.pg1.local_ip4,
538 psrc=self.pg1.remote_hosts[6].ip4,
541 self.pg2.add_stream(p)
542 self.pg_enable_capture(self.pg_interfaces)
545 rx = self.pg2.get_capture(1)
546 self.verify_arp_resp(
551 self.pg1.remote_hosts[6].ip4,
555 # An attached host route out of pg2 for an undiscovered hosts generates
556 # an ARP request with the unnumbered address as the source
558 att_unnum = VppIpRoute(
560 self.pg1.remote_hosts[7].ip4,
562 [VppRoutePath("0.0.0.0", self.pg2.sw_if_index)],
564 att_unnum.add_vpp_config()
567 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
568 / IP(src=self.pg0.remote_ip4, dst=self.pg1._remote_hosts[7].ip4)
569 / UDP(sport=1234, dport=1234)
573 self.pg0.add_stream(p)
574 self.pg_enable_capture(self.pg_interfaces)
577 rx = self.pg2.get_capture(1)
580 rx[0], self.pg2.local_mac, self.pg1.local_ip4, self.pg1._remote_hosts[7].ip4
583 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(
585 hwsrc=self.pg2.remote_mac,
586 pdst=self.pg1.local_ip4,
587 psrc=self.pg1.remote_hosts[7].ip4,
590 self.pg2.add_stream(p)
591 self.pg_enable_capture(self.pg_interfaces)
594 rx = self.pg2.get_capture(1)
595 self.verify_arp_resp(
600 self.pg1.remote_hosts[7].ip4,
604 # An attached host route as yet unresolved out of pg2 for an
605 # undiscovered host, an ARP requests begets a response.
607 att_unnum1 = VppIpRoute(
609 self.pg1.remote_hosts[8].ip4,
611 [VppRoutePath("0.0.0.0", self.pg2.sw_if_index)],
613 att_unnum1.add_vpp_config()
615 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(
617 hwsrc=self.pg2.remote_mac,
618 pdst=self.pg1.local_ip4,
619 psrc=self.pg1.remote_hosts[8].ip4,
622 self.pg2.add_stream(p)
623 self.pg_enable_capture(self.pg_interfaces)
626 rx = self.pg2.get_capture(1)
627 self.verify_arp_resp(
632 self.pg1.remote_hosts[8].ip4,
636 # Send an ARP request from one of the so-far unlearned remote hosts
640 Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1._remote_hosts[9].mac)
644 hwsrc=self.pg1._remote_hosts[9].mac,
645 pdst=self.pg1.local_ip4,
646 psrc=self.pg1._remote_hosts[9].ip4,
650 self.pg1.add_stream(p)
651 self.pg_enable_capture(self.pg_interfaces)
654 rx = self.pg1.get_capture(1)
655 self.verify_arp_resp(
658 self.pg1._remote_hosts[9].mac,
660 self.pg1._remote_hosts[9].ip4,
664 # Add a hierarchy of routes for a host in the sub-net.
665 # Should still get an ARP resp since the cover is attached
667 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1.remote_mac) / ARP(
669 hwsrc=self.pg1.remote_mac,
670 pdst=self.pg1.local_ip4,
671 psrc=self.pg1.remote_hosts[10].ip4,
676 self.pg1.remote_hosts[10].ip4,
678 [VppRoutePath(self.pg1.remote_hosts[10].ip4, self.pg1.sw_if_index)],
682 self.pg1.add_stream(p)
683 self.pg_enable_capture(self.pg_interfaces)
685 rx = self.pg1.get_capture(1)
686 self.verify_arp_resp(
691 self.pg1.remote_hosts[10].ip4,
696 self.pg1.remote_hosts[10].ip4,
698 [VppRoutePath(self.pg1.remote_hosts[10].ip4, self.pg1.sw_if_index)],
702 self.pg1.add_stream(p)
703 self.pg_enable_capture(self.pg_interfaces)
705 rx = self.pg1.get_capture(1)
706 self.verify_arp_resp(
711 self.pg1.remote_hosts[10].ip4,
715 # add an ARP entry that's not on the sub-net and so whose
716 # adj-fib fails the refinement check. then send an ARP request
720 self, self.pg0.sw_if_index, self.pg0.remote_mac, "100.100.100.50"
724 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
726 hwsrc=self.pg0.remote_mac,
727 psrc="100.100.100.50",
728 pdst=self.pg0.remote_ip4,
730 self.send_and_assert_no_replies(self.pg0, p, "ARP req for from failed adj-fib")
734 # 1 - don't respond to ARP request for address not within the
735 # interface's sub-net
736 # 1b - nor within the unnumbered subnet
737 # 1c - nor within the subnet of a different interface
739 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
741 hwsrc=self.pg0.remote_mac,
743 psrc=self.pg0.remote_ip4,
745 self.send_and_assert_no_replies(
746 self.pg0, p, "ARP req for non-local destination"
748 self.assertFalse(find_nbr(self, self.pg0.sw_if_index, "10.10.10.3"))
750 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(
752 hwsrc=self.pg2.remote_mac,
754 psrc=self.pg1.remote_hosts[7].ip4,
756 self.send_and_assert_no_replies(
757 self.pg0, p, "ARP req for non-local destination - unnum"
760 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
762 hwsrc=self.pg0.remote_mac,
763 pdst=self.pg1.local_ip4,
764 psrc=self.pg1.remote_ip4,
766 self.send_and_assert_no_replies(self.pg0, p, "ARP req diff sub-net")
767 self.assertFalse(find_nbr(self, self.pg0.sw_if_index, self.pg1.remote_ip4))
770 # 2 - don't respond to ARP request from an address not within the
771 # interface's sub-net
772 # 2b - to a proxied address
773 # 2c - not within a different interface's sub-net
774 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
776 hwsrc=self.pg0.remote_mac,
778 pdst=self.pg0.local_ip4,
780 self.send_and_assert_no_replies(self.pg0, p, "ARP req for non-local source")
781 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(
783 hwsrc=self.pg2.remote_mac,
785 pdst=self.pg0.local_ip4,
787 self.send_and_assert_no_replies(
788 self.pg0, p, "ARP req for non-local source - unnum"
790 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
792 hwsrc=self.pg0.remote_mac,
793 psrc=self.pg1.remote_ip4,
794 pdst=self.pg0.local_ip4,
796 self.send_and_assert_no_replies(self.pg0, p, "ARP req for non-local source 2c")
799 # 3 - don't respond to ARP request from an address that belongs to
802 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
804 hwsrc=self.pg0.remote_mac,
805 psrc=self.pg0.local_ip4,
806 pdst=self.pg0.local_ip4,
808 self.send_and_assert_no_replies(self.pg0, p, "ARP req for non-local source")
811 # 4 - don't respond to ARP requests that has mac source different
812 # from ARP request HW source
814 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
816 hwsrc="00:00:00:DE:AD:BE",
817 psrc=self.pg0.remote_ip4,
818 pdst=self.pg0.local_ip4,
820 self.send_and_assert_no_replies(self.pg0, p, "ARP req for non-local source")
823 # 5 - don't respond to ARP requests for address within the
824 # interface's sub-net but not the interface's address
826 self.pg0.generate_remote_hosts(2)
827 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
829 hwsrc=self.pg0.remote_mac,
830 psrc=self.pg0.remote_hosts[0].ip4,
831 pdst=self.pg0.remote_hosts[1].ip4,
833 self.send_and_assert_no_replies(
834 self.pg0, p, "ARP req for non-local destination"
840 static_arp.remove_vpp_config()
841 self.pg2.unset_unnumbered(self.pg1.sw_if_index)
843 # need this to flush the adj-fibs
844 self.pg2.unset_unnumbered(self.pg1.sw_if_index)
845 self.pg2.admin_down()
846 self.pg1.admin_down()
848 def test_proxy_mirror_arp(self):
849 """Interface Mirror Proxy ARP"""
852 # When VPP has an interface whose address is also applied to a TAP
853 # interface on the host, then VPP's TAP interface will be unnumbered
854 # to the 'real' interface and do proxy ARP from the host.
855 # the curious aspect of this setup is that ARP requests from the host
856 # will come from the VPP's own address.
858 self.pg0.generate_remote_hosts(2)
860 arp_req_from_me = Ether(src=self.pg2.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
862 hwsrc=self.pg2.remote_mac,
863 pdst=self.pg0.remote_hosts[1].ip4,
864 psrc=self.pg0.local_ip4,
868 # Configure Proxy ARP for the subnet on PG0addresses on pg0
870 self.vapi.proxy_arp_add_del(
873 "low": self.pg0._local_ip4_subnet,
874 "hi": self.pg0._local_ip4_bcast,
879 # Make pg2 un-numbered to pg0
881 self.pg2.set_unnumbered(self.pg0.sw_if_index)
884 # Enable pg2 for proxy ARP
886 self.pg2.set_proxy_arp()
889 # Send the ARP request with an originating address that
890 # is VPP's own address
892 rx = self.send_and_expect(self.pg2, [arp_req_from_me], self.pg2)
893 self.verify_arp_resp(
897 self.pg0.remote_hosts[1].ip4,
902 # validate we have not learned an ARP entry as a result of this
904 self.assertFalse(find_nbr(self, self.pg2.sw_if_index, self.pg0.local_ip4))
907 # setup a punt redirect so packets from the uplink go to the tap
909 redirect = VppIpPuntRedirect(
910 self, self.pg0.sw_if_index, self.pg2.sw_if_index, self.pg0.local_ip4
912 redirect.add_vpp_config()
916 src=self.pg0.remote_mac,
917 dst=self.pg0.local_mac,
919 / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
920 / TCP(sport=80, dport=80)
923 rx = self.send_and_expect(self.pg0, [p_tcp], self.pg2)
925 # there's no ARP entry so this is an ARP req
926 self.assertTrue(rx[0].haslayer(ARP))
928 # and ARP entry for VPP's pg0 address on the host interface
931 self.pg2.sw_if_index,
934 is_no_fib_entry=True,
936 # now the packets shold forward
937 rx = self.send_and_expect(self.pg0, [p_tcp], self.pg2)
938 self.assertFalse(rx[0].haslayer(ARP))
939 self.assertEqual(rx[0][Ether].dst, self.pg2.remote_mac)
942 # flush the neighbor cache on the uplink
944 af = VppEnum.vl_api_address_family_t
945 self.vapi.ip_neighbor_flush(af.ADDRESS_IP4, self.pg0.sw_if_index)
947 # ensure we can still resolve the ARPs on the uplink
948 self.pg0.resolve_arp()
950 self.assertTrue(find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_ip4))
955 self.vapi.proxy_arp_add_del(
958 "low": self.pg0._local_ip4_subnet,
959 "hi": self.pg0._local_ip4_bcast,
963 redirect.remove_vpp_config()
965 def test_proxy_arp(self):
968 self.pg1.generate_remote_hosts(2)
971 # Proxy ARP request packets for each interface
973 arp_req_pg0 = Ether(src=self.pg0.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
975 hwsrc=self.pg0.remote_mac,
977 psrc=self.pg0.remote_ip4,
979 arp_req_pg0_tagged = (
980 Ether(src=self.pg0.remote_mac, dst="ff:ff:ff:ff:ff:ff")
984 hwsrc=self.pg0.remote_mac,
986 psrc=self.pg0.remote_ip4,
989 arp_req_pg1 = Ether(src=self.pg1.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
991 hwsrc=self.pg1.remote_mac,
993 psrc=self.pg1.remote_ip4,
995 arp_req_pg2 = Ether(src=self.pg2.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
997 hwsrc=self.pg2.remote_mac,
999 psrc=self.pg1.remote_hosts[1].ip4,
1001 arp_req_pg3 = Ether(src=self.pg3.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
1003 hwsrc=self.pg3.remote_mac,
1005 psrc=self.pg3.remote_ip4,
1009 # Configure Proxy ARP for 10.10.10.0 -> 10.10.10.124
1011 self.vapi.proxy_arp_add_del(
1012 proxy={"table_id": 0, "low": "10.10.10.2", "hi": "10.10.10.124"}, is_add=1
1016 # No responses are sent when the interfaces are not enabled for proxy
1019 self.send_and_assert_no_replies(
1020 self.pg0, arp_req_pg0, "ARP req from unconfigured interface"
1022 self.send_and_assert_no_replies(
1023 self.pg2, arp_req_pg2, "ARP req from unconfigured interface"
1027 # Make pg2 un-numbered to pg1
1028 # still won't reply.
1030 self.pg2.set_unnumbered(self.pg1.sw_if_index)
1032 self.send_and_assert_no_replies(
1033 self.pg2, arp_req_pg2, "ARP req from unnumbered interface"
1037 # Enable each interface to reply to proxy ARPs
1039 for i in self.pg_interfaces:
1043 # Now each of the interfaces should reply to a request to a proxied
1046 self.pg0.add_stream(arp_req_pg0)
1047 self.pg_enable_capture(self.pg_interfaces)
1050 rx = self.pg0.get_capture(1)
1051 self.verify_arp_resp(
1054 self.pg0.remote_mac,
1056 self.pg0.remote_ip4,
1059 self.pg0.add_stream(arp_req_pg0_tagged)
1060 self.pg_enable_capture(self.pg_interfaces)
1063 rx = self.pg0.get_capture(1)
1064 self.verify_arp_resp(
1067 self.pg0.remote_mac,
1069 self.pg0.remote_ip4,
1072 self.pg1.add_stream(arp_req_pg1)
1073 self.pg_enable_capture(self.pg_interfaces)
1076 rx = self.pg1.get_capture(1)
1077 self.verify_arp_resp(
1080 self.pg1.remote_mac,
1082 self.pg1.remote_ip4,
1085 self.pg2.add_stream(arp_req_pg2)
1086 self.pg_enable_capture(self.pg_interfaces)
1089 rx = self.pg2.get_capture(1)
1090 self.verify_arp_resp(
1093 self.pg2.remote_mac,
1095 self.pg1.remote_hosts[1].ip4,
1099 # A request for an address out of the configured range
1101 arp_req_pg1_hi = Ether(src=self.pg1.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
1103 hwsrc=self.pg1.remote_mac,
1104 pdst="10.10.10.125",
1105 psrc=self.pg1.remote_ip4,
1107 self.send_and_assert_no_replies(
1108 self.pg1, arp_req_pg1_hi, "ARP req out of range HI"
1110 arp_req_pg1_low = Ether(src=self.pg1.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
1112 hwsrc=self.pg1.remote_mac,
1114 psrc=self.pg1.remote_ip4,
1116 self.send_and_assert_no_replies(
1117 self.pg1, arp_req_pg1_low, "ARP req out of range Low"
1121 # Request for an address in the proxy range but from an interface
1122 # in a different VRF
1124 self.send_and_assert_no_replies(
1125 self.pg3, arp_req_pg3, "ARP req from different VRF"
1129 # Disable Each interface for proxy ARP
1130 # - expect none to respond
1132 for i in self.pg_interfaces:
1135 self.send_and_assert_no_replies(self.pg0, arp_req_pg0, "ARP req from disable")
1136 self.send_and_assert_no_replies(self.pg1, arp_req_pg1, "ARP req from disable")
1137 self.send_and_assert_no_replies(self.pg2, arp_req_pg2, "ARP req from disable")
1140 # clean up on interface 2
1142 self.pg2.unset_unnumbered(self.pg1.sw_if_index)
1144 def test_mpls(self):
1148 # Interface 2 does not yet have ip4 config
1150 self.pg2.config_ip4()
1151 self.pg2.generate_remote_hosts(2)
1154 # Add a route with out going label via an ARP unresolved next-hop
1156 ip_10_0_0_1 = VppIpRoute(
1162 self.pg2.remote_hosts[1].ip4, self.pg2.sw_if_index, labels=[55]
1166 ip_10_0_0_1.add_vpp_config()
1169 # packets should generate an ARP request
1172 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1173 / IP(src=self.pg0.remote_ip4, dst="10.0.0.1")
1174 / UDP(sport=1234, dport=1234)
1175 / Raw(b"\xa5" * 100)
1178 self.pg0.add_stream(p)
1179 self.pg_enable_capture(self.pg_interfaces)
1182 rx = self.pg2.get_capture(1)
1183 self.verify_arp_req(
1184 rx[0], self.pg2.local_mac, self.pg2.local_ip4, self.pg2._remote_hosts[1].ip4
1188 # now resolve the neighbours
1190 self.pg2.configure_ipv4_neighbors()
1193 # Now packet should be properly MPLS encapped.
1194 # This verifies that MPLS link-type adjacencies are completed
1195 # when the ARP entry resolves
1197 self.pg0.add_stream(p)
1198 self.pg_enable_capture(self.pg_interfaces)
1201 rx = self.pg2.get_capture(1)
1202 self.verify_ip_o_mpls(
1205 self.pg2.remote_hosts[1].mac,
1207 self.pg0.remote_ip4,
1210 self.pg2.unconfig_ip4()
1212 def test_arp_vrrp(self):
1213 """ARP reply with VRRP virtual src hw addr"""
1216 # IP packet destined for pg1 remote host arrives on pg0 resulting
1217 # in an ARP request for the address of the remote host on pg1
1220 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1221 / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4)
1222 / UDP(sport=1234, dport=1234)
1226 rx1 = self.send_and_expect(self.pg0, [p0], self.pg1)
1228 self.verify_arp_req(
1229 rx1[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1.remote_ip4
1233 # ARP reply for address of pg1 remote host arrives on pg1 with
1234 # the hw src addr set to a value in the VRRP IPv4 range of
1237 p1 = Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / ARP(
1239 hwdst=self.pg1.local_mac,
1240 hwsrc="00:00:5e:00:01:09",
1241 pdst=self.pg1.local_ip4,
1242 psrc=self.pg1.remote_ip4,
1245 self.send_and_assert_no_replies(self.pg1, p1, "ARP reply")
1248 # IP packet destined for pg1 remote host arrives on pg0 again.
1249 # VPP should have an ARP entry for that address now and the packet
1250 # should be sent out pg1.
1252 rx1 = self.send_and_expect(self.pg0, [p0], self.pg1)
1257 "00:00:5e:00:01:09",
1258 self.pg0.remote_ip4,
1259 self.pg1.remote_ip4,
1262 self.pg1.admin_down()
1265 def test_arp_duplicates(self):
1266 """ARP Duplicates"""
1269 # Generate some hosts on the LAN
1271 self.pg1.generate_remote_hosts(3)
1274 # Add host 1 on pg1 and pg2
1276 arp_pg1 = VppNeighbor(
1278 self.pg1.sw_if_index,
1279 self.pg1.remote_hosts[1].mac,
1280 self.pg1.remote_hosts[1].ip4,
1282 arp_pg1.add_vpp_config()
1283 arp_pg2 = VppNeighbor(
1285 self.pg2.sw_if_index,
1286 self.pg2.remote_mac,
1287 self.pg1.remote_hosts[1].ip4,
1289 arp_pg2.add_vpp_config()
1292 # IP packet destined for pg1 remote host arrives on pg1 again.
1295 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1296 / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_hosts[1].ip4)
1297 / UDP(sport=1234, dport=1234)
1301 self.pg0.add_stream(p)
1302 self.pg_enable_capture(self.pg_interfaces)
1305 rx1 = self.pg1.get_capture(1)
1310 self.pg1.remote_hosts[1].mac,
1311 self.pg0.remote_ip4,
1312 self.pg1.remote_hosts[1].ip4,
1316 # remove the duplicate on pg1
1317 # packet stream should generate ARPs out of pg1
1319 arp_pg1.remove_vpp_config()
1321 self.pg0.add_stream(p)
1322 self.pg_enable_capture(self.pg_interfaces)
1325 rx1 = self.pg1.get_capture(1)
1327 self.verify_arp_req(
1328 rx1[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1.remote_hosts[1].ip4
1334 arp_pg1.add_vpp_config()
1336 self.pg0.add_stream(p)
1337 self.pg_enable_capture(self.pg_interfaces)
1340 rx1 = self.pg1.get_capture(1)
1345 self.pg1.remote_hosts[1].mac,
1346 self.pg0.remote_ip4,
1347 self.pg1.remote_hosts[1].ip4,
1350 def test_arp_static(self):
1352 self.pg2.generate_remote_hosts(3)
1355 # Add a static ARP entry
1357 static_arp = VppNeighbor(
1359 self.pg2.sw_if_index,
1360 self.pg2.remote_hosts[1].mac,
1361 self.pg2.remote_hosts[1].ip4,
1364 static_arp.add_vpp_config()
1367 # Add the connected prefix to the interface
1369 self.pg2.config_ip4()
1372 # We should now find the adj-fib
1376 self, self.pg2.sw_if_index, self.pg2.remote_hosts[1].ip4, is_static=1
1379 self.assertTrue(find_route(self, self.pg2.remote_hosts[1].ip4, 32))
1382 # remove the connected
1384 self.pg2.unconfig_ip4()
1387 # put the interface into table 1
1389 self.pg2.set_table_ip4(1)
1392 # configure the same connected and expect to find the
1393 # adj fib in the new table
1395 self.pg2.config_ip4()
1396 self.assertTrue(find_route(self, self.pg2.remote_hosts[1].ip4, 32, table_id=1))
1401 self.pg2.unconfig_ip4()
1402 static_arp.remove_vpp_config()
1403 self.pg2.set_table_ip4(0)
1405 def test_arp_static_replace_dynamic_same_mac(self):
1406 """ARP Static can replace Dynamic (same mac)"""
1407 self.pg2.generate_remote_hosts(1)
1409 dyn_arp = VppNeighbor(
1411 self.pg2.sw_if_index,
1412 self.pg2.remote_hosts[0].mac,
1413 self.pg2.remote_hosts[0].ip4,
1415 static_arp = VppNeighbor(
1417 self.pg2.sw_if_index,
1418 self.pg2.remote_hosts[0].mac,
1419 self.pg2.remote_hosts[0].ip4,
1424 # Add a dynamic ARP entry
1426 dyn_arp.add_vpp_config()
1429 # We should find the dynamic nbr
1433 self, self.pg2.sw_if_index, self.pg2.remote_hosts[0].ip4, is_static=1
1439 self.pg2.sw_if_index,
1440 self.pg2.remote_hosts[0].ip4,
1442 mac=self.pg2.remote_hosts[0].mac,
1447 # Add a static ARP entry with the same mac
1449 static_arp.add_vpp_config()
1452 # We should now find the static nbr with the same mac
1456 self, self.pg2.sw_if_index, self.pg2.remote_hosts[0].ip4, is_static=0
1462 self.pg2.sw_if_index,
1463 self.pg2.remote_hosts[0].ip4,
1465 mac=self.pg2.remote_hosts[0].mac,
1472 static_arp.remove_vpp_config()
1474 def test_arp_static_replace_dynamic_diff_mac(self):
1475 """ARP Static can replace Dynamic (diff mac)"""
1476 self.pg2.generate_remote_hosts(2)
1478 dyn_arp = VppNeighbor(
1480 self.pg2.sw_if_index,
1481 self.pg2.remote_hosts[0].mac,
1482 self.pg2.remote_hosts[0].ip4,
1484 static_arp = VppNeighbor(
1486 self.pg2.sw_if_index,
1487 self.pg2.remote_hosts[1].mac,
1488 self.pg2.remote_hosts[0].ip4,
1493 # Add a dynamic ARP entry
1495 dyn_arp.add_vpp_config()
1498 # We should find the dynamic nbr
1502 self, self.pg2.sw_if_index, self.pg2.remote_hosts[0].ip4, is_static=1
1508 self.pg2.sw_if_index,
1509 self.pg2.remote_hosts[0].ip4,
1511 mac=self.pg2.remote_hosts[0].mac,
1516 # Add a static ARP entry with a changed mac
1518 static_arp.add_vpp_config()
1521 # We should now find the static nbr with a changed mac
1525 self, self.pg2.sw_if_index, self.pg2.remote_hosts[0].ip4, is_static=0
1531 self.pg2.sw_if_index,
1532 self.pg2.remote_hosts[0].ip4,
1534 mac=self.pg2.remote_hosts[1].mac,
1541 static_arp.remove_vpp_config()
1543 def test_arp_incomplete(self):
1544 """ARP Incomplete"""
1545 self.pg1.generate_remote_hosts(4)
1548 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1549 / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_hosts[1].ip4)
1550 / UDP(sport=1234, dport=1234)
1554 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1555 / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_hosts[2].ip4)
1556 / UDP(sport=1234, dport=1234)
1560 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1561 / IP(src=self.pg0.remote_ip4, dst="1.1.1.1")
1562 / UDP(sport=1234, dport=1234)
1567 # a packet to an unresolved destination generates an ARP request
1569 rx = self.send_and_expect(self.pg0, [p0], self.pg1)
1570 self.verify_arp_req(
1571 rx[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1._remote_hosts[1].ip4
1575 # add a neighbour for remote host 1
1577 static_arp = VppNeighbor(
1579 self.pg1.sw_if_index,
1580 self.pg1.remote_hosts[1].mac,
1581 self.pg1.remote_hosts[1].ip4,
1584 static_arp.add_vpp_config()
1587 # add a route through remote host 3 hence we get an incomplete
1593 [VppRoutePath(self.pg1.remote_hosts[3].ip4, self.pg1.sw_if_index)],
1595 rx = self.send_and_expect(self.pg0, [p2], self.pg1)
1596 self.verify_arp_req(
1597 rx[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1._remote_hosts[3].ip4
1601 # change the interface's MAC
1603 self.vapi.sw_interface_set_mac_address(
1604 self.pg1.sw_if_index, "00:00:00:33:33:33"
1608 # now ARP requests come from the new source mac
1610 rx = self.send_and_expect(self.pg0, [p1], self.pg1)
1611 self.verify_arp_req(
1613 "00:00:00:33:33:33",
1615 self.pg1._remote_hosts[2].ip4,
1617 rx = self.send_and_expect(self.pg0, [p2], self.pg1)
1618 self.verify_arp_req(
1620 "00:00:00:33:33:33",
1622 self.pg1._remote_hosts[3].ip4,
1626 # packets to the resolved host also have the new source mac
1628 rx = self.send_and_expect(self.pg0, [p0], self.pg1)
1631 "00:00:00:33:33:33",
1632 self.pg1.remote_hosts[1].mac,
1633 self.pg0.remote_ip4,
1634 self.pg1.remote_hosts[1].ip4,
1638 # set the mac address on the interface that does not have a
1639 # configured subnet and thus no glean
1641 self.vapi.sw_interface_set_mac_address(
1642 self.pg2.sw_if_index, "00:00:00:33:33:33"
1645 def test_garp(self):
1649 # Generate some hosts on the LAN
1651 self.pg1.generate_remote_hosts(4)
1652 self.pg2.generate_remote_hosts(4)
1659 self.pg1.sw_if_index,
1660 self.pg1.remote_hosts[1].mac,
1661 self.pg1.remote_hosts[1].ip4,
1663 arp.add_vpp_config()
1668 self.pg1.sw_if_index,
1669 self.pg1.remote_hosts[1].ip4,
1670 mac=self.pg1.remote_hosts[1].mac,
1675 # Send a GARP (request) to swap the host 1's address to that of host 2
1677 p1 = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1.remote_hosts[2].mac) / ARP(
1679 hwdst=self.pg1.local_mac,
1680 hwsrc=self.pg1.remote_hosts[2].mac,
1681 pdst=self.pg1.remote_hosts[1].ip4,
1682 psrc=self.pg1.remote_hosts[1].ip4,
1685 self.pg1.add_stream(p1)
1686 self.pg_enable_capture(self.pg_interfaces)
1692 self.pg1.sw_if_index,
1693 self.pg1.remote_hosts[1].ip4,
1694 mac=self.pg1.remote_hosts[2].mac,
1697 self.assert_equal(self.get_arp_rx_garp(self.pg1), 1)
1700 # Send a GARP (reply) to swap the host 1's address to that of host 3
1702 p1 = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1.remote_hosts[3].mac) / ARP(
1704 hwdst=self.pg1.local_mac,
1705 hwsrc=self.pg1.remote_hosts[3].mac,
1706 pdst=self.pg1.remote_hosts[1].ip4,
1707 psrc=self.pg1.remote_hosts[1].ip4,
1710 self.pg1.add_stream(p1)
1711 self.pg_enable_capture(self.pg_interfaces)
1717 self.pg1.sw_if_index,
1718 self.pg1.remote_hosts[1].ip4,
1719 mac=self.pg1.remote_hosts[3].mac,
1722 self.assert_equal(self.get_arp_rx_garp(self.pg1), 2)
1725 # GARPs (request nor replies) for host we don't know yet
1726 # don't result in new neighbour entries
1728 p1 = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1.remote_hosts[3].mac) / ARP(
1730 hwdst=self.pg1.local_mac,
1731 hwsrc=self.pg1.remote_hosts[3].mac,
1732 pdst=self.pg1.remote_hosts[2].ip4,
1733 psrc=self.pg1.remote_hosts[2].ip4,
1736 self.pg1.add_stream(p1)
1737 self.pg_enable_capture(self.pg_interfaces)
1741 find_nbr(self, self.pg1.sw_if_index, self.pg1.remote_hosts[2].ip4)
1744 p1 = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1.remote_hosts[3].mac) / ARP(
1746 hwdst=self.pg1.local_mac,
1747 hwsrc=self.pg1.remote_hosts[3].mac,
1748 pdst=self.pg1.remote_hosts[2].ip4,
1749 psrc=self.pg1.remote_hosts[2].ip4,
1752 self.pg1.add_stream(p1)
1753 self.pg_enable_capture(self.pg_interfaces)
1757 find_nbr(self, self.pg1.sw_if_index, self.pg1.remote_hosts[2].ip4)
1761 # IP address in different subnets are not learnt
1763 self.pg2.configure_ipv4_neighbors()
1765 for op in ["is-at", "who-has"]:
1768 Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_hosts[1].mac)
1771 hwdst=self.pg2.local_mac,
1772 hwsrc=self.pg2.remote_hosts[1].mac,
1773 pdst=self.pg2.remote_hosts[1].ip4,
1774 psrc=self.pg2.remote_hosts[1].ip4,
1778 Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_hosts[1].mac)
1781 hwdst="ff:ff:ff:ff:ff:ff",
1782 hwsrc=self.pg2.remote_hosts[1].mac,
1783 pdst=self.pg2.remote_hosts[1].ip4,
1784 psrc=self.pg2.remote_hosts[1].ip4,
1789 self.send_and_assert_no_replies(self.pg1, p1)
1791 find_nbr(self, self.pg1.sw_if_index, self.pg2.remote_hosts[1].ip4)
1794 # they are all dropped because the subnet's don't match
1797 self.statistics.get_err_counter("/err/arp-reply/l3_dst_address_not_local"),
1800 def test_arp_incomplete2(self):
1801 """Incomplete Entries"""
1804 # ensure that we throttle the ARP and ND requests
1806 self.pg0.generate_remote_hosts(2)
1811 ip_10_0_0_1 = VppIpRoute(
1815 [VppRoutePath(self.pg0.remote_hosts[1].ip4, self.pg0.sw_if_index)],
1817 ip_10_0_0_1.add_vpp_config()
1820 Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
1821 / IP(src=self.pg1.remote_ip4, dst="10.0.0.1")
1822 / UDP(sport=1234, dport=1234)
1826 self.pg1.add_stream(p1 * 257)
1827 self.pg_enable_capture(self.pg_interfaces)
1829 rx = self.pg0._get_capture(1)
1832 # how many we get is going to be dependent on the time for packet
1833 # processing but it should be small
1835 self.assertLess(len(rx), 64)
1840 ip_10_1 = VppIpRoute(
1846 self.pg0.remote_hosts[1].ip6,
1847 self.pg0.sw_if_index,
1848 proto=DpoProto.DPO_PROTO_IP6,
1852 ip_10_1.add_vpp_config()
1855 Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
1856 / IPv6(src=self.pg1.remote_ip6, dst="10::1")
1857 / UDP(sport=1234, dport=1234)
1861 self.pg1.add_stream(p1 * 257)
1862 self.pg_enable_capture(self.pg_interfaces)
1864 rx = self.pg0._get_capture(1)
1867 # how many we get is going to be dependent on the time for packet
1868 # processing but it should be small
1870 self.assertLess(len(rx), 64)
1872 def test_arp_forus(self):
1873 """ARP for for-us"""
1876 # Test that VPP responds with ARP requests to addresses that
1877 # are connected and local routes.
1878 # Use one of the 'remote' addresses in the subnet as a local address
1879 # The intention of this route is that it then acts like a secondary
1880 # address added to an interface
1882 self.pg0.generate_remote_hosts(2)
1886 self.pg0.remote_hosts[1].ip4,
1891 self.pg0.sw_if_index,
1892 type=FibPathType.FIB_PATH_TYPE_LOCAL,
1896 forus.add_vpp_config()
1898 p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
1900 hwdst=self.pg0.local_mac,
1901 hwsrc=self.pg0.remote_mac,
1902 pdst=self.pg0.remote_hosts[1].ip4,
1903 psrc=self.pg0.remote_ip4,
1906 rx = self.send_and_expect(self.pg0, [p], self.pg0)
1908 self.verify_arp_resp(
1911 self.pg0.remote_mac,
1912 self.pg0.remote_hosts[1].ip4,
1913 self.pg0.remote_ip4,
1916 def test_arp_table_swap(self):
1918 # Generate some hosts on the LAN
1921 self.pg1.generate_remote_hosts(N_NBRS)
1923 for n in range(N_NBRS):
1924 # a route thru each neighbour
1929 [VppRoutePath(self.pg1.remote_hosts[n].ip4, self.pg1.sw_if_index)],
1932 # resolve each neighbour
1933 p1 = Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / ARP(
1935 hwdst=self.pg1.local_mac,
1936 hwsrc="00:00:5e:00:01:09",
1937 pdst=self.pg1.local_ip4,
1938 psrc=self.pg1.remote_hosts[n].ip4,
1941 self.send_and_assert_no_replies(self.pg1, p1, "ARP reply")
1943 self.logger.info(self.vapi.cli("sh ip neighbors"))
1946 # swap the table pg1 is in
1948 table = VppIpTable(self, 100).add_vpp_config()
1950 self.pg1.unconfig_ip4()
1951 self.pg1.set_table_ip4(100)
1952 self.pg1.config_ip4()
1955 # all neighbours are cleared
1957 for n in range(N_NBRS):
1959 find_nbr(self, self.pg1.sw_if_index, self.pg1.remote_hosts[n].ip4)
1963 # packets to all neighbours generate ARP requests
1965 for n in range(N_NBRS):
1966 # a route thru each neighbour
1971 [VppRoutePath(self.pg1.remote_hosts[n].ip4, self.pg1.sw_if_index)],
1976 Ether(src=self.pg1.remote_hosts[n].mac, dst=self.pg1.local_mac)
1977 / IP(src=self.pg1.remote_hosts[n].ip4, dst="10.0.0.%d" % n)
1980 rxs = self.send_and_expect(self.pg1, [p], self.pg1)
1982 self.verify_arp_req(
1986 self.pg1.remote_hosts[n].ip4,
1989 self.pg1.unconfig_ip4()
1990 self.pg1.set_table_ip4(0)
1992 def test_glean_src_select(self):
1993 """Multi Connecteds"""
1996 # configure multiple connected subnets on an interface
1997 # and ensure that ARP requests for hosts on those subnets
1998 # pick up the correct source address
2000 conn1 = VppIpInterfaceAddress(self, self.pg1, "10.0.0.1", 24).add_vpp_config()
2001 conn2 = VppIpInterfaceAddress(self, self.pg1, "10.0.1.1", 24).add_vpp_config()
2004 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2005 / IP(src=self.pg1.remote_ip4, dst="10.0.0.128")
2009 rxs = self.send_and_expect(self.pg0, [p1], self.pg1)
2011 self.verify_arp_req(rx, self.pg1.local_mac, "10.0.0.1", "10.0.0.128")
2014 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2015 / IP(src=self.pg1.remote_ip4, dst="10.0.1.128")
2019 rxs = self.send_and_expect(self.pg0, [p2], self.pg1)
2021 self.verify_arp_req(rx, self.pg1.local_mac, "10.0.1.1", "10.0.1.128")
2024 # add a local address in the same subnet
2025 # the source addresses are equivalent. VPP happens to
2026 # choose the last one that was added
2027 conn3 = VppIpInterfaceAddress(self, self.pg1, "10.0.1.2", 24).add_vpp_config()
2029 rxs = self.send_and_expect(self.pg0, [p2], self.pg1)
2031 self.verify_arp_req(rx, self.pg1.local_mac, "10.0.1.2", "10.0.1.128")
2036 conn3.remove_vpp_config()
2037 rxs = self.send_and_expect(self.pg0, [p2], self.pg1)
2039 self.verify_arp_req(rx, self.pg1.local_mac, "10.0.1.1", "10.0.1.128")
2042 # add back, this time remove the first one
2044 conn3 = VppIpInterfaceAddress(self, self.pg1, "10.0.1.2", 24).add_vpp_config()
2046 rxs = self.send_and_expect(self.pg0, [p2], self.pg1)
2048 self.verify_arp_req(rx, self.pg1.local_mac, "10.0.1.2", "10.0.1.128")
2050 conn1.remove_vpp_config()
2051 rxs = self.send_and_expect(self.pg0, [p2], self.pg1)
2053 self.verify_arp_req(rx, self.pg1.local_mac, "10.0.1.2", "10.0.1.128")
2055 # apply a connected prefix to an interface in a different table
2060 [VppRoutePath("0.0.0.0", self.pg1.sw_if_index)],
2064 rxs = self.send_and_expect(self.pg3, [p2], self.pg1)
2066 self.verify_arp_req(rx, self.pg1.local_mac, "10.0.1.2", "10.0.1.128")
2069 conn3.remove_vpp_config()
2070 conn2.remove_vpp_config()
2073 @tag_fixme_vpp_workers
2074 class NeighborStatsTestCase(VppTestCase):
2075 """ARP/ND Counters"""
2078 def setUpClass(cls):
2079 super(NeighborStatsTestCase, cls).setUpClass()
2082 def tearDownClass(cls):
2083 super(NeighborStatsTestCase, cls).tearDownClass()
2086 super(NeighborStatsTestCase, self).setUp()
2088 self.create_pg_interfaces(range(2))
2090 # pg0 configured with ip4 and 6 addresses used for input
2091 # pg1 configured with ip4 and 6 addresses used for output
2092 # pg2 is unnumbered to pg0
2093 for i in self.pg_interfaces:
2101 super(NeighborStatsTestCase, self).tearDown()
2103 for i in self.pg_interfaces:
2108 def test_arp_stats(self):
2111 self.vapi.cli("adj counters enable")
2112 self.pg1.generate_remote_hosts(2)
2116 self.pg1.sw_if_index,
2117 self.pg1.remote_hosts[0].mac,
2118 self.pg1.remote_hosts[0].ip4,
2120 arp1.add_vpp_config()
2123 self.pg1.sw_if_index,
2124 self.pg1.remote_hosts[1].mac,
2125 self.pg1.remote_hosts[1].ip4,
2127 arp2.add_vpp_config()
2130 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2131 / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_hosts[0].ip4)
2132 / UDP(sport=1234, dport=1234)
2136 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2137 / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_hosts[1].ip4)
2138 / UDP(sport=1234, dport=1234)
2142 rx = self.send_and_expect(self.pg0, p1 * NUM_PKTS, self.pg1)
2143 rx = self.send_and_expect(self.pg0, p2 * NUM_PKTS, self.pg1)
2145 self.assertEqual(NUM_PKTS, arp1.get_stats()["packets"])
2146 self.assertEqual(NUM_PKTS, arp2.get_stats()["packets"])
2148 rx = self.send_and_expect(self.pg0, p1 * NUM_PKTS, self.pg1)
2149 self.assertEqual(NUM_PKTS * 2, arp1.get_stats()["packets"])
2151 def test_nd_stats(self):
2154 self.vapi.cli("adj counters enable")
2155 self.pg0.generate_remote_hosts(3)
2159 self.pg0.sw_if_index,
2160 self.pg0.remote_hosts[1].mac,
2161 self.pg0.remote_hosts[1].ip6,
2163 nd1.add_vpp_config()
2166 self.pg0.sw_if_index,
2167 self.pg0.remote_hosts[2].mac,
2168 self.pg0.remote_hosts[2].ip6,
2170 nd2.add_vpp_config()
2173 Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
2174 / IPv6(src=self.pg1.remote_ip6, dst=self.pg0.remote_hosts[1].ip6)
2175 / UDP(sport=1234, dport=1234)
2179 Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
2180 / IPv6(src=self.pg1.remote_ip6, dst=self.pg0.remote_hosts[2].ip6)
2181 / UDP(sport=1234, dport=1234)
2185 rx = self.send_and_expect(self.pg1, p1 * 16, self.pg0)
2186 rx = self.send_and_expect(self.pg1, p2 * 16, self.pg0)
2188 self.assertEqual(16, nd1.get_stats()["packets"])
2189 self.assertEqual(16, nd2.get_stats()["packets"])
2191 rx = self.send_and_expect(self.pg1, p1 * NUM_PKTS, self.pg0)
2192 self.assertEqual(NUM_PKTS + 16, nd1.get_stats()["packets"])
2195 class NeighborAgeTestCase(VppTestCase):
2199 def setUpClass(cls):
2200 super(NeighborAgeTestCase, cls).setUpClass()
2203 def tearDownClass(cls):
2204 super(NeighborAgeTestCase, cls).tearDownClass()
2207 super(NeighborAgeTestCase, self).setUp()
2209 self.create_pg_interfaces(range(1))
2211 # pg0 configured with ip4 and 6 addresses used for input
2212 # pg1 configured with ip4 and 6 addresses used for output
2213 # pg2 is unnumbered to pg0
2214 for i in self.pg_interfaces:
2222 super(NeighborAgeTestCase, self).tearDown()
2224 for i in self.pg_interfaces:
2229 def verify_arp_req(self, rx, smac, sip, dip):
2231 self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
2232 self.assertEqual(ether.src, smac)
2235 self.assertEqual(arp.hwtype, 1)
2236 self.assertEqual(arp.ptype, 0x800)
2237 self.assertEqual(arp.hwlen, 6)
2238 self.assertEqual(arp.plen, 4)
2239 self.assertEqual(arp.op, arp_opts["who-has"])
2240 self.assertEqual(arp.hwsrc, smac)
2241 self.assertEqual(arp.hwdst, "00:00:00:00:00:00")
2242 self.assertEqual(arp.psrc, sip)
2243 self.assertEqual(arp.pdst, dip)
2248 self.vapi.cli("set logging unthrottle 0")
2249 self.vapi.cli("set logging size %d" % 0xFFFF)
2251 self.pg0.generate_remote_hosts(201)
2253 vaf = VppEnum.vl_api_address_family_t
2256 # start listening on all interfaces
2258 self.pg_enable_capture(self.pg_interfaces)
2261 # Set the neighbor configuration:
2266 self.vapi.ip_neighbor_config(
2267 af=vaf.ADDRESS_IP4, max_number=200, max_age=0, recycle=False
2270 self.vapi.cli("sh ip neighbor-config")
2272 # add the 198 neighbours that should pass (-1 for one created in setup)
2273 for ii in range(200):
2276 self.pg0.sw_if_index,
2277 self.pg0.remote_hosts[ii].mac,
2278 self.pg0.remote_hosts[ii].ip4,
2281 # one more neighbor over the limit should fail
2282 with self.vapi.assert_negative_api_retval():
2285 self.pg0.sw_if_index,
2286 self.pg0.remote_hosts[200].mac,
2287 self.pg0.remote_hosts[200].ip4,
2291 # change the config to allow recycling the old neighbors
2293 self.vapi.ip_neighbor_config(
2294 af=vaf.ADDRESS_IP4, max_number=200, max_age=0, recycle=True
2297 # now new additions are allowed
2300 self.pg0.sw_if_index,
2301 self.pg0.remote_hosts[200].mac,
2302 self.pg0.remote_hosts[200].ip4,
2305 # add the first neighbor we configured has been re-used
2307 find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[0].ip4)
2310 find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[200].ip4)
2314 # change the config to age old neighbors
2316 self.vapi.ip_neighbor_config(
2317 af=vaf.ADDRESS_IP4, max_number=200, max_age=2, recycle=True
2320 self.vapi.cli("sh ip4 neighbor-sorted")
2323 self.virtual_sleep(3)
2326 # expect probes from all these ARP entries as they age
2327 # 3 probes for each neighbor 3*200 = 600
2328 rxs = self.pg0.get_capture(600, timeout=2)
2331 for jj in range(200):
2332 rx = rxs[ii * 200 + jj]
2336 # 3 probes sent then 1 more second to see if a reply comes, before
2339 self.virtual_sleep(1)
2342 self.vapi.ip_neighbor_dump(sw_if_index=0xFFFFFFFF, af=vaf.ADDRESS_IP4)
2346 # load up some neighbours again with 2s aging enabled
2347 # they should be removed after 10s (2s age + 4s for probes + gap)
2348 # check for the add and remove events
2350 enum = VppEnum.vl_api_ip_neighbor_event_flags_t
2352 self.vapi.want_ip_neighbor_events_v2(enable=1)
2353 for ii in range(10):
2356 self.pg0.sw_if_index,
2357 self.pg0.remote_hosts[ii].mac,
2358 self.pg0.remote_hosts[ii].ip4,
2361 e = self.vapi.wait_for_event(1, "ip_neighbor_event_v2")
2362 self.assertEqual(e.flags, enum.IP_NEIGHBOR_API_EVENT_FLAG_ADDED)
2363 self.assertEqual(str(e.neighbor.ip_address), self.pg0.remote_hosts[ii].ip4)
2364 self.assertEqual(e.neighbor.mac_address, self.pg0.remote_hosts[ii].mac)
2366 self.virtual_sleep(10)
2368 self.vapi.ip_neighbor_dump(sw_if_index=0xFFFFFFFF, af=vaf.ADDRESS_IP4)
2372 for ii in range(10):
2373 e = self.vapi.wait_for_event(1, "ip_neighbor_event_v2")
2374 self.assertEqual(e.flags, enum.IP_NEIGHBOR_API_EVENT_FLAG_REMOVED)
2377 # check we got the correct mac/ip pairs - done separately
2378 # because we don't care about the order the remove notifications
2380 for ii in range(10):
2382 mac = self.pg0.remote_hosts[ii].mac
2383 ip = self.pg0.remote_hosts[ii].ip4
2386 if e.neighbor.mac_address == mac and str(e.neighbor.ip_address) == ip:
2389 self.assertTrue(found)
2392 # check if we can set age and recycle with empty neighbor list
2394 self.vapi.ip_neighbor_config(
2395 af=vaf.ADDRESS_IP4, max_number=200, max_age=1000, recycle=True
2399 # load up some neighbours again, then disable the aging
2400 # they should still be there in 10 seconds time
2402 for ii in range(10):
2405 self.pg0.sw_if_index,
2406 self.pg0.remote_hosts[ii].mac,
2407 self.pg0.remote_hosts[ii].ip4,
2409 self.vapi.ip_neighbor_config(
2410 af=vaf.ADDRESS_IP4, max_number=200, max_age=0, recycle=False
2413 self.virtual_sleep(10)
2415 find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[0].ip4)
2419 class NeighborReplaceTestCase(VppTestCase):
2420 """ARP/ND Replacement"""
2423 def setUpClass(cls):
2424 super(NeighborReplaceTestCase, cls).setUpClass()
2427 def tearDownClass(cls):
2428 super(NeighborReplaceTestCase, cls).tearDownClass()
2431 super(NeighborReplaceTestCase, self).setUp()
2433 self.create_pg_interfaces(range(4))
2435 # pg0 configured with ip4 and 6 addresses used for input
2436 # pg1 configured with ip4 and 6 addresses used for output
2437 # pg2 is unnumbered to pg0
2438 for i in self.pg_interfaces:
2446 super(NeighborReplaceTestCase, self).tearDown()
2448 for i in self.pg_interfaces:
2453 def test_replace(self):
2458 for i in self.pg_interfaces:
2459 i.generate_remote_hosts(N_HOSTS)
2460 i.configure_ipv4_neighbors()
2461 i.configure_ipv6_neighbors()
2464 self.vapi.ip_neighbor_replace_begin()
2465 self.vapi.ip_neighbor_replace_end()
2467 for i in self.pg_interfaces:
2468 for h in range(N_HOSTS):
2470 find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[h].ip4)
2473 find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[h].ip6)
2477 # and them all back via the API
2479 for i in self.pg_interfaces:
2480 for h in range(N_HOSTS):
2482 self, i.sw_if_index, i.remote_hosts[h].mac, i.remote_hosts[h].ip4
2485 self, i.sw_if_index, i.remote_hosts[h].mac, i.remote_hosts[h].ip6
2489 # begin the replacement again, this time touch some
2490 # the neighbours on pg1 so they are not deleted
2492 self.vapi.ip_neighbor_replace_begin()
2494 # update from the API all neighbours on pg1
2495 for h in range(N_HOSTS):
2498 self.pg1.sw_if_index,
2499 self.pg1.remote_hosts[h].mac,
2500 self.pg1.remote_hosts[h].ip4,
2504 self.pg1.sw_if_index,
2505 self.pg1.remote_hosts[h].mac,
2506 self.pg1.remote_hosts[h].ip6,
2509 # update from the data-plane all neighbours on pg3
2510 self.pg3.configure_ipv4_neighbors()
2511 self.pg3.configure_ipv6_neighbors()
2513 # complete the replacement
2514 self.logger.info(self.vapi.cli("sh ip neighbors"))
2515 self.vapi.ip_neighbor_replace_end()
2517 for i in self.pg_interfaces:
2518 if i == self.pg1 or i == self.pg3:
2519 # neighbours on pg1 and pg3 are still present
2520 for h in range(N_HOSTS):
2522 find_nbr(self, i.sw_if_index, i.remote_hosts[h].ip4)
2525 find_nbr(self, i.sw_if_index, i.remote_hosts[h].ip6)
2528 # all other neighbours are toast
2529 for h in range(N_HOSTS):
2531 find_nbr(self, i.sw_if_index, i.remote_hosts[h].ip4)
2534 find_nbr(self, i.sw_if_index, i.remote_hosts[h].ip6)
2538 class NeighborFlush(VppTestCase):
2539 """Neighbor Flush"""
2542 def setUpClass(cls):
2543 super(NeighborFlush, cls).setUpClass()
2546 def tearDownClass(cls):
2547 super(NeighborFlush, cls).tearDownClass()
2550 super(NeighborFlush, self).setUp()
2552 self.create_pg_interfaces(range(2))
2554 for i in self.pg_interfaces:
2562 super(NeighborFlush, self).tearDown()
2564 for i in self.pg_interfaces:
2569 def test_flush(self):
2570 """Neighbour Flush"""
2573 nf = e.vl_api_ip_neighbor_flags_t
2574 af = e.vl_api_address_family_t
2576 static = [False, True]
2577 self.pg0.generate_remote_hosts(N_HOSTS)
2578 self.pg1.generate_remote_hosts(N_HOSTS)
2581 # a few v4 and v6 dynamic neoghbors
2582 for n in range(N_HOSTS):
2585 self.pg0.sw_if_index,
2586 self.pg0.remote_hosts[n].mac,
2587 self.pg0.remote_hosts[n].ip4,
2592 self.pg1.sw_if_index,
2593 self.pg1.remote_hosts[n].mac,
2594 self.pg1.remote_hosts[n].ip6,
2598 # flush the interfaces individually
2599 self.vapi.ip_neighbor_flush(af.ADDRESS_IP4, self.pg0.sw_if_index)
2601 # check we haven't flushed that which we shouldn't
2602 for n in range(N_HOSTS):
2606 self.pg1.sw_if_index,
2607 self.pg1.remote_hosts[n].ip6,
2612 self.vapi.ip_neighbor_flush(af.ADDRESS_IP6, self.pg1.sw_if_index)
2614 for n in range(N_HOSTS):
2616 find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[n].ip4)
2619 find_nbr(self, self.pg1.sw_if_index, self.pg1.remote_hosts[n].ip6)
2622 # add the nieghbours back
2623 for n in range(N_HOSTS):
2626 self.pg0.sw_if_index,
2627 self.pg0.remote_hosts[n].mac,
2628 self.pg0.remote_hosts[n].ip4,
2633 self.pg1.sw_if_index,
2634 self.pg1.remote_hosts[n].mac,
2635 self.pg1.remote_hosts[n].ip6,
2639 self.logger.info(self.vapi.cli("sh ip neighbor"))
2641 # flush both interfaces at the same time
2642 self.vapi.ip_neighbor_flush(af.ADDRESS_IP6, 0xFFFFFFFF)
2644 # check we haven't flushed that which we shouldn't
2645 for n in range(N_HOSTS):
2649 self.pg0.sw_if_index,
2650 self.pg0.remote_hosts[n].ip4,
2655 self.vapi.ip_neighbor_flush(af.ADDRESS_IP4, 0xFFFFFFFF)
2657 for n in range(N_HOSTS):
2659 find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[n].ip4)
2662 find_nbr(self, self.pg1.sw_if_index, self.pg1.remote_hosts[n].ip6)
2666 if __name__ == "__main__":
2667 unittest.main(testRunner=VppTestRunner)