64be36d739adae8fe2d4b7aa145ce87813fe1604
[vpp.git] / test / test_neighbor.py
1 #!/usr/bin/env python3
2
3 import unittest
4 import os
5 from socket import AF_INET, AF_INET6, inet_pton
6
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 (
11     VppIpRoute,
12     VppRoutePath,
13     find_route,
14     VppIpTable,
15     DpoProto,
16     FibPathType,
17     VppIpInterfaceAddress,
18 )
19 from vpp_papi import VppEnum
20 from vpp_ip import VppIpPuntRedirect
21
22 import scapy.compat
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
29
30
31 NUM_PKTS = 67
32
33 # not exported by scapy, so redefined here
34 arp_opts = {"who-has": 1, "is-at": 2}
35
36
37 class ARPTestCase(VppTestCase):
38     """ARP Test Case"""
39
40     @classmethod
41     def setUpClass(cls):
42         super(ARPTestCase, cls).setUpClass()
43
44     @classmethod
45     def tearDownClass(cls):
46         super(ARPTestCase, cls).tearDownClass()
47
48     def setUp(self):
49         super(ARPTestCase, self).setUp()
50
51         # create 3 pg interfaces
52         self.create_pg_interfaces(range(4))
53
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:
58             i.admin_up()
59
60         self.pg0.config_ip4()
61         self.pg0.config_ip6()
62         self.pg0.resolve_arp()
63
64         self.pg1.config_ip4()
65         self.pg1.config_ip6()
66
67         # pg3 in a different VRF
68         self.tbl = VppIpTable(self, 1)
69         self.tbl.add_vpp_config()
70
71         self.pg3.set_table_ip4(1)
72         self.pg3.config_ip4()
73
74     def tearDown(self):
75         self.pg0.unconfig_ip4()
76         self.pg0.unconfig_ip6()
77
78         self.pg1.unconfig_ip4()
79         self.pg1.unconfig_ip6()
80
81         self.pg3.unconfig_ip4()
82         self.pg3.set_table_ip4(0)
83
84         for i in self.pg_interfaces:
85             i.admin_down()
86
87         super(ARPTestCase, self).tearDown()
88
89     def verify_arp_req(self, rx, smac, sip, dip):
90         ether = rx[Ether]
91         self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
92         self.assertEqual(ether.src, smac)
93         self.assertEqual(ether.type, 0x0806)
94
95         arp = rx[ARP]
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)
105
106     def verify_arp_resp(self, rx, smac, dmac, sip, dip):
107         ether = rx[Ether]
108         self.assertEqual(ether.dst, dmac)
109         self.assertEqual(ether.src, smac)
110         self.assertEqual(ether.type, 0x0806)
111
112         arp = rx[ARP]
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)
122
123     def verify_arp_vrrp_resp(self, rx, smac, dmac, sip, dip):
124         ether = rx[Ether]
125         self.assertEqual(ether.dst, dmac)
126         self.assertEqual(ether.src, smac)
127
128         arp = rx[ARP]
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)
139
140     def verify_ip(self, rx, smac, dmac, sip, dip):
141         ether = rx[Ether]
142         self.assertEqual(ether.dst, dmac)
143         self.assertEqual(ether.src, smac)
144         self.assertEqual(ether.type, 0x0800)
145
146         ip = rx[IP]
147         self.assertEqual(ip.src, sip)
148         self.assertEqual(ip.dst, dip)
149
150     def verify_ip_o_mpls(self, rx, smac, dmac, label, sip, dip):
151         ether = rx[Ether]
152         self.assertEqual(ether.dst, dmac)
153         self.assertEqual(ether.src, smac)
154         self.assertEqual(ether.type, 0x8847)
155
156         mpls = rx[MPLS]
157         self.assertTrue(mpls.label, label)
158
159         ip = rx[IP]
160         self.assertEqual(ip.src, sip)
161         self.assertEqual(ip.dst, dip)
162
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()
166
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()
170
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()
174
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()
178
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()
182
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()
186
187     def test_arp(self):
188         """ARP"""
189
190         #
191         # Generate some hosts on the LAN
192         #
193         self.pg1.generate_remote_hosts(11)
194
195         #
196         # watch for:
197         #  - all neighbour events
198         #  - all neighbor events on pg1
199         #  - neighbor events for host[1] on pg1
200         #
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
204         )
205         self.vapi.want_ip_neighbor_events(
206             enable=1,
207             pid=os.getpid(),
208             sw_if_index=self.pg1.sw_if_index,
209             ip=self.pg1.remote_hosts[1].ip4,
210         )
211
212         self.logger.info(self.vapi.cli("sh ip neighbor-watcher"))
213
214         #
215         # Send IP traffic to one of these unresolved hosts.
216         #  expect the generation of an ARP request
217         #
218         p = (
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)
222             / Raw()
223         )
224
225         self.pg0.add_stream(p)
226         self.pg_enable_capture(self.pg_interfaces)
227         self.pg_start()
228
229         rx = self.pg1.get_capture(1)
230
231         self.verify_arp_req(
232             rx[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1._remote_hosts[1].ip4
233         )
234
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)
238
239         #
240         # And a dynamic ARP entry for host 1
241         #
242         dyn_arp = VppNeighbor(
243             self,
244             self.pg1.sw_if_index,
245             self.pg1.remote_hosts[1].mac,
246             self.pg1.remote_hosts[1].ip4,
247         )
248         dyn_arp.add_vpp_config()
249         self.assertTrue(dyn_arp.query_vpp_config())
250
251         self.logger.info(self.vapi.cli("show ip neighbor-watcher"))
252
253         # this matches all of the listnerers
254         es = [self.vapi.wait_for_event(1, "ip_neighbor_event") for i in range(3)]
255         for e in es:
256             self.assertEqual(str(e.neighbor.ip_address), self.pg1.remote_hosts[1].ip4)
257
258         #
259         # now we expect IP traffic forwarded
260         #
261         dyn_p = (
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)
265             / Raw()
266         )
267
268         self.pg0.add_stream(dyn_p)
269         self.pg_enable_capture(self.pg_interfaces)
270         self.pg_start()
271
272         rx = self.pg1.get_capture(1)
273
274         self.verify_ip(
275             rx[0],
276             self.pg1.local_mac,
277             self.pg1.remote_hosts[1].mac,
278             self.pg0.remote_ip4,
279             self.pg1._remote_hosts[1].ip4,
280         )
281
282         #
283         # And a Static ARP entry for host 2
284         #
285         static_arp = VppNeighbor(
286             self,
287             self.pg1.sw_if_index,
288             self.pg1.remote_hosts[2].mac,
289             self.pg1.remote_hosts[2].ip4,
290             is_static=1,
291         )
292         static_arp.add_vpp_config()
293         es = [self.vapi.wait_for_event(1, "ip_neighbor_event") for i in range(2)]
294         for e in es:
295             self.assertEqual(str(e.neighbor.ip_address), self.pg1.remote_hosts[2].ip4)
296
297         static_p = (
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)
301             / Raw()
302         )
303
304         self.pg0.add_stream(static_p)
305         self.pg_enable_capture(self.pg_interfaces)
306         self.pg_start()
307
308         rx = self.pg1.get_capture(1)
309
310         self.verify_ip(
311             rx[0],
312             self.pg1.local_mac,
313             self.pg1.remote_hosts[2].mac,
314             self.pg0.remote_ip4,
315             self.pg1._remote_hosts[2].ip4,
316         )
317
318         #
319         # remove all the listeners
320         #
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
324         )
325         self.vapi.want_ip_neighbor_events(
326             enable=0,
327             pid=os.getpid(),
328             sw_if_index=self.pg1.sw_if_index,
329             ip=self.pg1.remote_hosts[1].ip4,
330         )
331
332         #
333         # flap the link. dynamic ARPs get flush, statics don't
334         #
335         self.pg1.admin_down()
336         self.pg1.admin_up()
337
338         self.pg0.add_stream(static_p)
339         self.pg_enable_capture(self.pg_interfaces)
340         self.pg_start()
341         rx = self.pg1.get_capture(1)
342
343         self.verify_ip(
344             rx[0],
345             self.pg1.local_mac,
346             self.pg1.remote_hosts[2].mac,
347             self.pg0.remote_ip4,
348             self.pg1._remote_hosts[2].ip4,
349         )
350
351         self.pg0.add_stream(dyn_p)
352         self.pg_enable_capture(self.pg_interfaces)
353         self.pg_start()
354
355         rx = self.pg1.get_capture(1)
356         self.verify_arp_req(
357             rx[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1._remote_hosts[1].ip4
358         )
359         self.assert_equal(self.get_arp_tx_requests(self.pg1), 2)
360
361         self.assertFalse(dyn_arp.query_vpp_config())
362         self.assertTrue(static_arp.query_vpp_config())
363         #
364         # Send an ARP request from one of the so-far unlearned remote hosts
365         #
366         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1._remote_hosts[3].mac) / ARP(
367             op="who-has",
368             hwsrc=self.pg1._remote_hosts[3].mac,
369             pdst=self.pg1.local_ip4,
370             psrc=self.pg1._remote_hosts[3].ip4,
371         )
372
373         self.pg1.add_stream(p)
374         self.pg_enable_capture(self.pg_interfaces)
375         self.pg_start()
376
377         rx = self.pg1.get_capture(1)
378         self.verify_arp_resp(
379             rx[0],
380             self.pg1.local_mac,
381             self.pg1._remote_hosts[3].mac,
382             self.pg1.local_ip4,
383             self.pg1._remote_hosts[3].ip4,
384         )
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)
388
389         #
390         # VPP should have learned the mapping for the remote host
391         #
392         self.assertTrue(
393             find_nbr(self, self.pg1.sw_if_index, self.pg1._remote_hosts[3].ip4)
394         )
395         #
396         # Fire in an ARP request before the interface becomes IP enabled
397         #
398         self.pg2.generate_remote_hosts(4)
399
400         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(
401             op="who-has",
402             hwsrc=self.pg2.remote_mac,
403             pdst=self.pg1.local_ip4,
404             psrc=self.pg2.remote_hosts[3].ip4,
405         )
406         pt = (
407             Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac)
408             / Dot1Q(vlan=0)
409             / ARP(
410                 op="who-has",
411                 hwsrc=self.pg2.remote_mac,
412                 pdst=self.pg1.local_ip4,
413                 psrc=self.pg2.remote_hosts[3].ip4,
414             )
415         )
416         self.send_and_assert_no_replies(self.pg2, p, "interface not IP enabled")
417
418         #
419         # Make pg2 un-numbered to pg1
420         #
421         self.pg2.set_unnumbered(self.pg1.sw_if_index)
422
423         #
424         # test the unnumbered dump both by all interfaces and just the enabled
425         # one
426         #
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)
435
436         #
437         # We should respond to ARP requests for the unnumbered to address
438         # once an attached route to the source is known
439         #
440         self.send_and_assert_no_replies(
441             self.pg2, p, "ARP req for unnumbered address - no source"
442         )
443
444         attached_host = VppIpRoute(
445             self,
446             self.pg2.remote_hosts[3].ip4,
447             32,
448             [VppRoutePath("0.0.0.0", self.pg2.sw_if_index)],
449         )
450         attached_host.add_vpp_config()
451
452         self.pg2.add_stream(p)
453         self.pg_enable_capture(self.pg_interfaces)
454         self.pg_start()
455
456         rx = self.pg2.get_capture(1)
457         self.verify_arp_resp(
458             rx[0],
459             self.pg2.local_mac,
460             self.pg2.remote_mac,
461             self.pg1.local_ip4,
462             self.pg2.remote_hosts[3].ip4,
463         )
464
465         self.pg2.add_stream(pt)
466         self.pg_enable_capture(self.pg_interfaces)
467         self.pg_start()
468
469         rx = self.pg2.get_capture(1)
470         self.verify_arp_resp(
471             rx[0],
472             self.pg2.local_mac,
473             self.pg2.remote_mac,
474             self.pg1.local_ip4,
475             self.pg2.remote_hosts[3].ip4,
476         )
477
478         #
479         # A neighbor entry that has no associated FIB-entry
480         #
481         arp_no_fib = VppNeighbor(
482             self,
483             self.pg1.sw_if_index,
484             self.pg1.remote_hosts[4].mac,
485             self.pg1.remote_hosts[4].ip4,
486             is_no_fib_entry=1,
487         )
488         arp_no_fib.add_vpp_config()
489
490         #
491         # check we have the neighbor, but no route
492         #
493         self.assertTrue(
494             find_nbr(self, self.pg1.sw_if_index, self.pg1._remote_hosts[4].ip4)
495         )
496         self.assertFalse(find_route(self, self.pg1._remote_hosts[4].ip4, 32))
497         #
498         # pg2 is unnumbered to pg1, so we can form adjacencies out of pg2
499         # from within pg1's subnet
500         #
501         arp_unnum = VppNeighbor(
502             self,
503             self.pg2.sw_if_index,
504             self.pg1.remote_hosts[5].mac,
505             self.pg1.remote_hosts[5].ip4,
506         )
507         arp_unnum.add_vpp_config()
508
509         p = (
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)
513             / Raw()
514         )
515
516         self.pg0.add_stream(p)
517         self.pg_enable_capture(self.pg_interfaces)
518         self.pg_start()
519
520         rx = self.pg2.get_capture(1)
521
522         self.verify_ip(
523             rx[0],
524             self.pg2.local_mac,
525             self.pg1.remote_hosts[5].mac,
526             self.pg0.remote_ip4,
527             self.pg1._remote_hosts[5].ip4,
528         )
529
530         #
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
533         #
534         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(
535             op="who-has",
536             hwsrc=self.pg2.remote_mac,
537             pdst=self.pg1.local_ip4,
538             psrc=self.pg1.remote_hosts[6].ip4,
539         )
540
541         self.pg2.add_stream(p)
542         self.pg_enable_capture(self.pg_interfaces)
543         self.pg_start()
544
545         rx = self.pg2.get_capture(1)
546         self.verify_arp_resp(
547             rx[0],
548             self.pg2.local_mac,
549             self.pg2.remote_mac,
550             self.pg1.local_ip4,
551             self.pg1.remote_hosts[6].ip4,
552         )
553
554         #
555         # An attached host route out of pg2 for an undiscovered hosts generates
556         # an ARP request with the unnumbered address as the source
557         #
558         att_unnum = VppIpRoute(
559             self,
560             self.pg1.remote_hosts[7].ip4,
561             32,
562             [VppRoutePath("0.0.0.0", self.pg2.sw_if_index)],
563         )
564         att_unnum.add_vpp_config()
565
566         p = (
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)
570             / Raw()
571         )
572
573         self.pg0.add_stream(p)
574         self.pg_enable_capture(self.pg_interfaces)
575         self.pg_start()
576
577         rx = self.pg2.get_capture(1)
578
579         self.verify_arp_req(
580             rx[0], self.pg2.local_mac, self.pg1.local_ip4, self.pg1._remote_hosts[7].ip4
581         )
582
583         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(
584             op="who-has",
585             hwsrc=self.pg2.remote_mac,
586             pdst=self.pg1.local_ip4,
587             psrc=self.pg1.remote_hosts[7].ip4,
588         )
589
590         self.pg2.add_stream(p)
591         self.pg_enable_capture(self.pg_interfaces)
592         self.pg_start()
593
594         rx = self.pg2.get_capture(1)
595         self.verify_arp_resp(
596             rx[0],
597             self.pg2.local_mac,
598             self.pg2.remote_mac,
599             self.pg1.local_ip4,
600             self.pg1.remote_hosts[7].ip4,
601         )
602
603         #
604         # An attached host route as yet unresolved out of pg2 for an
605         # undiscovered host, an ARP requests begets a response.
606         #
607         att_unnum1 = VppIpRoute(
608             self,
609             self.pg1.remote_hosts[8].ip4,
610             32,
611             [VppRoutePath("0.0.0.0", self.pg2.sw_if_index)],
612         )
613         att_unnum1.add_vpp_config()
614
615         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(
616             op="who-has",
617             hwsrc=self.pg2.remote_mac,
618             pdst=self.pg1.local_ip4,
619             psrc=self.pg1.remote_hosts[8].ip4,
620         )
621
622         self.pg2.add_stream(p)
623         self.pg_enable_capture(self.pg_interfaces)
624         self.pg_start()
625
626         rx = self.pg2.get_capture(1)
627         self.verify_arp_resp(
628             rx[0],
629             self.pg2.local_mac,
630             self.pg2.remote_mac,
631             self.pg1.local_ip4,
632             self.pg1.remote_hosts[8].ip4,
633         )
634
635         #
636         # Send an ARP request from one of the so-far unlearned remote hosts
637         # with a VLAN0 tag
638         #
639         p = (
640             Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1._remote_hosts[9].mac)
641             / Dot1Q(vlan=0)
642             / ARP(
643                 op="who-has",
644                 hwsrc=self.pg1._remote_hosts[9].mac,
645                 pdst=self.pg1.local_ip4,
646                 psrc=self.pg1._remote_hosts[9].ip4,
647             )
648         )
649
650         self.pg1.add_stream(p)
651         self.pg_enable_capture(self.pg_interfaces)
652         self.pg_start()
653
654         rx = self.pg1.get_capture(1)
655         self.verify_arp_resp(
656             rx[0],
657             self.pg1.local_mac,
658             self.pg1._remote_hosts[9].mac,
659             self.pg1.local_ip4,
660             self.pg1._remote_hosts[9].ip4,
661         )
662
663         #
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
666         #
667         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1.remote_mac) / ARP(
668             op="who-has",
669             hwsrc=self.pg1.remote_mac,
670             pdst=self.pg1.local_ip4,
671             psrc=self.pg1.remote_hosts[10].ip4,
672         )
673
674         r1 = VppIpRoute(
675             self,
676             self.pg1.remote_hosts[10].ip4,
677             30,
678             [VppRoutePath(self.pg1.remote_hosts[10].ip4, self.pg1.sw_if_index)],
679         )
680         r1.add_vpp_config()
681
682         self.pg1.add_stream(p)
683         self.pg_enable_capture(self.pg_interfaces)
684         self.pg_start()
685         rx = self.pg1.get_capture(1)
686         self.verify_arp_resp(
687             rx[0],
688             self.pg1.local_mac,
689             self.pg1.remote_mac,
690             self.pg1.local_ip4,
691             self.pg1.remote_hosts[10].ip4,
692         )
693
694         r2 = VppIpRoute(
695             self,
696             self.pg1.remote_hosts[10].ip4,
697             32,
698             [VppRoutePath(self.pg1.remote_hosts[10].ip4, self.pg1.sw_if_index)],
699         )
700         r2.add_vpp_config()
701
702         self.pg1.add_stream(p)
703         self.pg_enable_capture(self.pg_interfaces)
704         self.pg_start()
705         rx = self.pg1.get_capture(1)
706         self.verify_arp_resp(
707             rx[0],
708             self.pg1.local_mac,
709             self.pg1.remote_mac,
710             self.pg1.local_ip4,
711             self.pg1.remote_hosts[10].ip4,
712         )
713
714         #
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
717         # from that source
718         #
719         a1 = VppNeighbor(
720             self, self.pg0.sw_if_index, self.pg0.remote_mac, "100.100.100.50"
721         )
722         a1.add_vpp_config()
723
724         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
725             op="who-has",
726             hwsrc=self.pg0.remote_mac,
727             psrc="100.100.100.50",
728             pdst=self.pg0.remote_ip4,
729         )
730         self.send_and_assert_no_replies(self.pg0, p, "ARP req for from failed adj-fib")
731
732         #
733         # ERROR Cases
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
738         #
739         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
740             op="who-has",
741             hwsrc=self.pg0.remote_mac,
742             pdst="10.10.10.3",
743             psrc=self.pg0.remote_ip4,
744         )
745         self.send_and_assert_no_replies(
746             self.pg0, p, "ARP req for non-local destination"
747         )
748         self.assertFalse(find_nbr(self, self.pg0.sw_if_index, "10.10.10.3"))
749
750         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(
751             op="who-has",
752             hwsrc=self.pg2.remote_mac,
753             pdst="10.10.10.3",
754             psrc=self.pg1.remote_hosts[7].ip4,
755         )
756         self.send_and_assert_no_replies(
757             self.pg0, p, "ARP req for non-local destination - unnum"
758         )
759
760         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
761             op="who-has",
762             hwsrc=self.pg0.remote_mac,
763             pdst=self.pg1.local_ip4,
764             psrc=self.pg1.remote_ip4,
765         )
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))
768
769         #
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(
775             op="who-has",
776             hwsrc=self.pg0.remote_mac,
777             psrc="10.10.10.3",
778             pdst=self.pg0.local_ip4,
779         )
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(
782             op="who-has",
783             hwsrc=self.pg2.remote_mac,
784             psrc="10.10.10.3",
785             pdst=self.pg0.local_ip4,
786         )
787         self.send_and_assert_no_replies(
788             self.pg0, p, "ARP req for non-local source - unnum"
789         )
790         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
791             op="who-has",
792             hwsrc=self.pg0.remote_mac,
793             psrc=self.pg1.remote_ip4,
794             pdst=self.pg0.local_ip4,
795         )
796         self.send_and_assert_no_replies(self.pg0, p, "ARP req for non-local source 2c")
797
798         #
799         #  3 - don't respond to ARP request from an address that belongs to
800         #      the router
801         #
802         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
803             op="who-has",
804             hwsrc=self.pg0.remote_mac,
805             psrc=self.pg0.local_ip4,
806             pdst=self.pg0.local_ip4,
807         )
808         self.send_and_assert_no_replies(self.pg0, p, "ARP req for non-local source")
809
810         #
811         #  4 - don't respond to ARP requests that has mac source different
812         #      from ARP request HW source
813         #
814         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
815             op="who-has",
816             hwsrc="00:00:00:DE:AD:BE",
817             psrc=self.pg0.remote_ip4,
818             pdst=self.pg0.local_ip4,
819         )
820         self.send_and_assert_no_replies(self.pg0, p, "ARP req for non-local source")
821
822         #
823         #  5 - don't respond to ARP requests for address within the
824         #      interface's sub-net but not the interface's address
825         #
826         self.pg0.generate_remote_hosts(2)
827         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
828             op="who-has",
829             hwsrc=self.pg0.remote_mac,
830             psrc=self.pg0.remote_hosts[0].ip4,
831             pdst=self.pg0.remote_hosts[1].ip4,
832         )
833         self.send_and_assert_no_replies(
834             self.pg0, p, "ARP req for non-local destination"
835         )
836
837         #
838         # cleanup
839         #
840         static_arp.remove_vpp_config()
841         self.pg2.unset_unnumbered(self.pg1.sw_if_index)
842
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()
847
848     def test_proxy_mirror_arp(self):
849         """Interface Mirror Proxy ARP"""
850
851         #
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.
857         #
858         self.pg0.generate_remote_hosts(2)
859
860         arp_req_from_me = Ether(src=self.pg2.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
861             op="who-has",
862             hwsrc=self.pg2.remote_mac,
863             pdst=self.pg0.remote_hosts[1].ip4,
864             psrc=self.pg0.local_ip4,
865         )
866
867         #
868         # Configure Proxy ARP for the subnet on PG0addresses on pg0
869         #
870         self.vapi.proxy_arp_add_del(
871             proxy={
872                 "table_id": 0,
873                 "low": self.pg0._local_ip4_subnet,
874                 "hi": self.pg0._local_ip4_bcast,
875             },
876             is_add=1,
877         )
878
879         # Make pg2 un-numbered to pg0
880         #
881         self.pg2.set_unnumbered(self.pg0.sw_if_index)
882
883         #
884         # Enable pg2 for proxy ARP
885         #
886         self.pg2.set_proxy_arp()
887
888         #
889         # Send the ARP request with an originating address that
890         # is VPP's own address
891         #
892         rx = self.send_and_expect(self.pg2, [arp_req_from_me], self.pg2)
893         self.verify_arp_resp(
894             rx[0],
895             self.pg2.local_mac,
896             self.pg2.remote_mac,
897             self.pg0.remote_hosts[1].ip4,
898             self.pg0.local_ip4,
899         )
900
901         #
902         # validate we have not learned an ARP entry as a result of this
903         #
904         self.assertFalse(find_nbr(self, self.pg2.sw_if_index, self.pg0.local_ip4))
905
906         #
907         # setup a punt redirect so packets from the uplink go to the tap
908         #
909         redirect = VppIpPuntRedirect(
910             self, self.pg0.sw_if_index, self.pg2.sw_if_index, self.pg0.local_ip4
911         )
912         redirect.add_vpp_config()
913
914         p_tcp = (
915             Ether(
916                 src=self.pg0.remote_mac,
917                 dst=self.pg0.local_mac,
918             )
919             / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
920             / TCP(sport=80, dport=80)
921             / Raw()
922         )
923         rx = self.send_and_expect(self.pg0, [p_tcp], self.pg2)
924
925         # there's no ARP entry so this is an ARP req
926         self.assertTrue(rx[0].haslayer(ARP))
927
928         # and ARP entry for VPP's pg0 address on the host interface
929         n1 = VppNeighbor(
930             self,
931             self.pg2.sw_if_index,
932             self.pg2.remote_mac,
933             self.pg0.local_ip4,
934             is_no_fib_entry=True,
935         ).add_vpp_config()
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)
940
941         #
942         # flush the neighbor cache on the uplink
943         #
944         af = VppEnum.vl_api_address_family_t
945         self.vapi.ip_neighbor_flush(af.ADDRESS_IP4, self.pg0.sw_if_index)
946
947         # ensure we can still resolve the ARPs on the uplink
948         self.pg0.resolve_arp()
949
950         self.assertTrue(find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_ip4))
951
952         #
953         # cleanup
954         #
955         self.vapi.proxy_arp_add_del(
956             proxy={
957                 "table_id": 0,
958                 "low": self.pg0._local_ip4_subnet,
959                 "hi": self.pg0._local_ip4_bcast,
960             },
961             is_add=0,
962         )
963         redirect.remove_vpp_config()
964
965     def test_proxy_arp(self):
966         """Proxy ARP"""
967
968         self.pg1.generate_remote_hosts(2)
969
970         #
971         # Proxy ARP request packets for each interface
972         #
973         arp_req_pg0 = Ether(src=self.pg0.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
974             op="who-has",
975             hwsrc=self.pg0.remote_mac,
976             pdst="10.10.10.3",
977             psrc=self.pg0.remote_ip4,
978         )
979         arp_req_pg0_tagged = (
980             Ether(src=self.pg0.remote_mac, dst="ff:ff:ff:ff:ff:ff")
981             / Dot1Q(vlan=0)
982             / ARP(
983                 op="who-has",
984                 hwsrc=self.pg0.remote_mac,
985                 pdst="10.10.10.3",
986                 psrc=self.pg0.remote_ip4,
987             )
988         )
989         arp_req_pg1 = Ether(src=self.pg1.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
990             op="who-has",
991             hwsrc=self.pg1.remote_mac,
992             pdst="10.10.10.3",
993             psrc=self.pg1.remote_ip4,
994         )
995         arp_req_pg2 = Ether(src=self.pg2.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
996             op="who-has",
997             hwsrc=self.pg2.remote_mac,
998             pdst="10.10.10.3",
999             psrc=self.pg1.remote_hosts[1].ip4,
1000         )
1001         arp_req_pg3 = Ether(src=self.pg3.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
1002             op="who-has",
1003             hwsrc=self.pg3.remote_mac,
1004             pdst="10.10.10.3",
1005             psrc=self.pg3.remote_ip4,
1006         )
1007
1008         #
1009         # Configure Proxy ARP for 10.10.10.0 -> 10.10.10.124
1010         #
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
1013         )
1014
1015         #
1016         # No responses are sent when the interfaces are not enabled for proxy
1017         # ARP
1018         #
1019         self.send_and_assert_no_replies(
1020             self.pg0, arp_req_pg0, "ARP req from unconfigured interface"
1021         )
1022         self.send_and_assert_no_replies(
1023             self.pg2, arp_req_pg2, "ARP req from unconfigured interface"
1024         )
1025
1026         #
1027         # Make pg2 un-numbered to pg1
1028         #  still won't reply.
1029         #
1030         self.pg2.set_unnumbered(self.pg1.sw_if_index)
1031
1032         self.send_and_assert_no_replies(
1033             self.pg2, arp_req_pg2, "ARP req from unnumbered interface"
1034         )
1035
1036         #
1037         # Enable each interface to reply to proxy ARPs
1038         #
1039         for i in self.pg_interfaces:
1040             i.set_proxy_arp()
1041
1042         #
1043         # Now each of the interfaces should reply to a request to a proxied
1044         # address
1045         #
1046         self.pg0.add_stream(arp_req_pg0)
1047         self.pg_enable_capture(self.pg_interfaces)
1048         self.pg_start()
1049
1050         rx = self.pg0.get_capture(1)
1051         self.verify_arp_resp(
1052             rx[0],
1053             self.pg0.local_mac,
1054             self.pg0.remote_mac,
1055             "10.10.10.3",
1056             self.pg0.remote_ip4,
1057         )
1058
1059         self.pg0.add_stream(arp_req_pg0_tagged)
1060         self.pg_enable_capture(self.pg_interfaces)
1061         self.pg_start()
1062
1063         rx = self.pg0.get_capture(1)
1064         self.verify_arp_resp(
1065             rx[0],
1066             self.pg0.local_mac,
1067             self.pg0.remote_mac,
1068             "10.10.10.3",
1069             self.pg0.remote_ip4,
1070         )
1071
1072         self.pg1.add_stream(arp_req_pg1)
1073         self.pg_enable_capture(self.pg_interfaces)
1074         self.pg_start()
1075
1076         rx = self.pg1.get_capture(1)
1077         self.verify_arp_resp(
1078             rx[0],
1079             self.pg1.local_mac,
1080             self.pg1.remote_mac,
1081             "10.10.10.3",
1082             self.pg1.remote_ip4,
1083         )
1084
1085         self.pg2.add_stream(arp_req_pg2)
1086         self.pg_enable_capture(self.pg_interfaces)
1087         self.pg_start()
1088
1089         rx = self.pg2.get_capture(1)
1090         self.verify_arp_resp(
1091             rx[0],
1092             self.pg2.local_mac,
1093             self.pg2.remote_mac,
1094             "10.10.10.3",
1095             self.pg1.remote_hosts[1].ip4,
1096         )
1097
1098         #
1099         # A request for an address out of the configured range
1100         #
1101         arp_req_pg1_hi = Ether(src=self.pg1.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
1102             op="who-has",
1103             hwsrc=self.pg1.remote_mac,
1104             pdst="10.10.10.125",
1105             psrc=self.pg1.remote_ip4,
1106         )
1107         self.send_and_assert_no_replies(
1108             self.pg1, arp_req_pg1_hi, "ARP req out of range HI"
1109         )
1110         arp_req_pg1_low = Ether(src=self.pg1.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
1111             op="who-has",
1112             hwsrc=self.pg1.remote_mac,
1113             pdst="10.10.10.1",
1114             psrc=self.pg1.remote_ip4,
1115         )
1116         self.send_and_assert_no_replies(
1117             self.pg1, arp_req_pg1_low, "ARP req out of range Low"
1118         )
1119
1120         #
1121         # Request for an address in the proxy range but from an interface
1122         # in a different VRF
1123         #
1124         self.send_and_assert_no_replies(
1125             self.pg3, arp_req_pg3, "ARP req from different VRF"
1126         )
1127
1128         #
1129         # Disable Each interface for proxy ARP
1130         #  - expect none to respond
1131         #
1132         for i in self.pg_interfaces:
1133             i.set_proxy_arp(0)
1134
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")
1138
1139         #
1140         # clean up on interface 2
1141         #
1142         self.pg2.unset_unnumbered(self.pg1.sw_if_index)
1143
1144     def test_mpls(self):
1145         """MPLS"""
1146
1147         #
1148         # Interface 2 does not yet have ip4 config
1149         #
1150         self.pg2.config_ip4()
1151         self.pg2.generate_remote_hosts(2)
1152
1153         #
1154         # Add a route with out going label via an ARP unresolved next-hop
1155         #
1156         ip_10_0_0_1 = VppIpRoute(
1157             self,
1158             "10.0.0.1",
1159             32,
1160             [
1161                 VppRoutePath(
1162                     self.pg2.remote_hosts[1].ip4, self.pg2.sw_if_index, labels=[55]
1163                 )
1164             ],
1165         )
1166         ip_10_0_0_1.add_vpp_config()
1167
1168         #
1169         # packets should generate an ARP request
1170         #
1171         p = (
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)
1176         )
1177
1178         self.pg0.add_stream(p)
1179         self.pg_enable_capture(self.pg_interfaces)
1180         self.pg_start()
1181
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
1185         )
1186
1187         #
1188         # now resolve the neighbours
1189         #
1190         self.pg2.configure_ipv4_neighbors()
1191
1192         #
1193         # Now packet should be properly MPLS encapped.
1194         #  This verifies that MPLS link-type adjacencies are completed
1195         #  when the ARP entry resolves
1196         #
1197         self.pg0.add_stream(p)
1198         self.pg_enable_capture(self.pg_interfaces)
1199         self.pg_start()
1200
1201         rx = self.pg2.get_capture(1)
1202         self.verify_ip_o_mpls(
1203             rx[0],
1204             self.pg2.local_mac,
1205             self.pg2.remote_hosts[1].mac,
1206             55,
1207             self.pg0.remote_ip4,
1208             "10.0.0.1",
1209         )
1210         self.pg2.unconfig_ip4()
1211
1212     def test_arp_vrrp(self):
1213         """ARP reply with VRRP virtual src hw addr"""
1214
1215         #
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
1218         #
1219         p0 = (
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)
1223             / Raw()
1224         )
1225
1226         rx1 = self.send_and_expect(self.pg0, [p0], self.pg1)
1227
1228         self.verify_arp_req(
1229             rx1[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1.remote_ip4
1230         )
1231
1232         #
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
1235         # MAC addresses
1236         #
1237         p1 = Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / ARP(
1238             op="is-at",
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,
1243         )
1244
1245         self.send_and_assert_no_replies(self.pg1, p1, "ARP reply")
1246
1247         #
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.
1251         #
1252         rx1 = self.send_and_expect(self.pg0, [p0], self.pg1)
1253
1254         self.verify_ip(
1255             rx1[0],
1256             self.pg1.local_mac,
1257             "00:00:5e:00:01:09",
1258             self.pg0.remote_ip4,
1259             self.pg1.remote_ip4,
1260         )
1261
1262         self.pg1.admin_down()
1263         self.pg1.admin_up()
1264
1265     def test_arp_duplicates(self):
1266         """ARP Duplicates"""
1267
1268         #
1269         # Generate some hosts on the LAN
1270         #
1271         self.pg1.generate_remote_hosts(3)
1272
1273         #
1274         # Add host 1 on pg1 and pg2
1275         #
1276         arp_pg1 = VppNeighbor(
1277             self,
1278             self.pg1.sw_if_index,
1279             self.pg1.remote_hosts[1].mac,
1280             self.pg1.remote_hosts[1].ip4,
1281         )
1282         arp_pg1.add_vpp_config()
1283         arp_pg2 = VppNeighbor(
1284             self,
1285             self.pg2.sw_if_index,
1286             self.pg2.remote_mac,
1287             self.pg1.remote_hosts[1].ip4,
1288         )
1289         arp_pg2.add_vpp_config()
1290
1291         #
1292         # IP packet destined for pg1 remote host arrives on pg1 again.
1293         #
1294         p = (
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)
1298             / Raw()
1299         )
1300
1301         self.pg0.add_stream(p)
1302         self.pg_enable_capture(self.pg_interfaces)
1303         self.pg_start()
1304
1305         rx1 = self.pg1.get_capture(1)
1306
1307         self.verify_ip(
1308             rx1[0],
1309             self.pg1.local_mac,
1310             self.pg1.remote_hosts[1].mac,
1311             self.pg0.remote_ip4,
1312             self.pg1.remote_hosts[1].ip4,
1313         )
1314
1315         #
1316         # remove the duplicate on pg1
1317         # packet stream should generate ARPs out of pg1
1318         #
1319         arp_pg1.remove_vpp_config()
1320
1321         self.pg0.add_stream(p)
1322         self.pg_enable_capture(self.pg_interfaces)
1323         self.pg_start()
1324
1325         rx1 = self.pg1.get_capture(1)
1326
1327         self.verify_arp_req(
1328             rx1[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1.remote_hosts[1].ip4
1329         )
1330
1331         #
1332         # Add it back
1333         #
1334         arp_pg1.add_vpp_config()
1335
1336         self.pg0.add_stream(p)
1337         self.pg_enable_capture(self.pg_interfaces)
1338         self.pg_start()
1339
1340         rx1 = self.pg1.get_capture(1)
1341
1342         self.verify_ip(
1343             rx1[0],
1344             self.pg1.local_mac,
1345             self.pg1.remote_hosts[1].mac,
1346             self.pg0.remote_ip4,
1347             self.pg1.remote_hosts[1].ip4,
1348         )
1349
1350     def test_arp_static(self):
1351         """ARP Static"""
1352         self.pg2.generate_remote_hosts(3)
1353
1354         #
1355         # Add a static ARP entry
1356         #
1357         static_arp = VppNeighbor(
1358             self,
1359             self.pg2.sw_if_index,
1360             self.pg2.remote_hosts[1].mac,
1361             self.pg2.remote_hosts[1].ip4,
1362             is_static=1,
1363         )
1364         static_arp.add_vpp_config()
1365
1366         #
1367         # Add the connected prefix to the interface
1368         #
1369         self.pg2.config_ip4()
1370
1371         #
1372         # We should now find the adj-fib
1373         #
1374         self.assertTrue(
1375             find_nbr(
1376                 self, self.pg2.sw_if_index, self.pg2.remote_hosts[1].ip4, is_static=1
1377             )
1378         )
1379         self.assertTrue(find_route(self, self.pg2.remote_hosts[1].ip4, 32))
1380
1381         #
1382         # remove the connected
1383         #
1384         self.pg2.unconfig_ip4()
1385
1386         #
1387         # put the interface into table 1
1388         #
1389         self.pg2.set_table_ip4(1)
1390
1391         #
1392         # configure the same connected and expect to find the
1393         # adj fib in the new table
1394         #
1395         self.pg2.config_ip4()
1396         self.assertTrue(find_route(self, self.pg2.remote_hosts[1].ip4, 32, table_id=1))
1397
1398         #
1399         # clean-up
1400         #
1401         self.pg2.unconfig_ip4()
1402         static_arp.remove_vpp_config()
1403         self.pg2.set_table_ip4(0)
1404
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)
1408
1409         dyn_arp = VppNeighbor(
1410             self,
1411             self.pg2.sw_if_index,
1412             self.pg2.remote_hosts[0].mac,
1413             self.pg2.remote_hosts[0].ip4,
1414         )
1415         static_arp = VppNeighbor(
1416             self,
1417             self.pg2.sw_if_index,
1418             self.pg2.remote_hosts[0].mac,
1419             self.pg2.remote_hosts[0].ip4,
1420             is_static=1,
1421         )
1422
1423         #
1424         # Add a dynamic ARP entry
1425         #
1426         dyn_arp.add_vpp_config()
1427
1428         #
1429         # We should find the dynamic nbr
1430         #
1431         self.assertFalse(
1432             find_nbr(
1433                 self, self.pg2.sw_if_index, self.pg2.remote_hosts[0].ip4, is_static=1
1434             )
1435         )
1436         self.assertTrue(
1437             find_nbr(
1438                 self,
1439                 self.pg2.sw_if_index,
1440                 self.pg2.remote_hosts[0].ip4,
1441                 is_static=0,
1442                 mac=self.pg2.remote_hosts[0].mac,
1443             )
1444         )
1445
1446         #
1447         # Add a static ARP entry with the same mac
1448         #
1449         static_arp.add_vpp_config()
1450
1451         #
1452         # We should now find the static nbr with the same mac
1453         #
1454         self.assertFalse(
1455             find_nbr(
1456                 self, self.pg2.sw_if_index, self.pg2.remote_hosts[0].ip4, is_static=0
1457             )
1458         )
1459         self.assertTrue(
1460             find_nbr(
1461                 self,
1462                 self.pg2.sw_if_index,
1463                 self.pg2.remote_hosts[0].ip4,
1464                 is_static=1,
1465                 mac=self.pg2.remote_hosts[0].mac,
1466             )
1467         )
1468
1469         #
1470         # clean-up
1471         #
1472         static_arp.remove_vpp_config()
1473
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)
1477
1478         dyn_arp = VppNeighbor(
1479             self,
1480             self.pg2.sw_if_index,
1481             self.pg2.remote_hosts[0].mac,
1482             self.pg2.remote_hosts[0].ip4,
1483         )
1484         static_arp = VppNeighbor(
1485             self,
1486             self.pg2.sw_if_index,
1487             self.pg2.remote_hosts[1].mac,
1488             self.pg2.remote_hosts[0].ip4,
1489             is_static=1,
1490         )
1491
1492         #
1493         # Add a dynamic ARP entry
1494         #
1495         dyn_arp.add_vpp_config()
1496
1497         #
1498         # We should find the dynamic nbr
1499         #
1500         self.assertFalse(
1501             find_nbr(
1502                 self, self.pg2.sw_if_index, self.pg2.remote_hosts[0].ip4, is_static=1
1503             )
1504         )
1505         self.assertTrue(
1506             find_nbr(
1507                 self,
1508                 self.pg2.sw_if_index,
1509                 self.pg2.remote_hosts[0].ip4,
1510                 is_static=0,
1511                 mac=self.pg2.remote_hosts[0].mac,
1512             )
1513         )
1514
1515         #
1516         # Add a static ARP entry with a changed mac
1517         #
1518         static_arp.add_vpp_config()
1519
1520         #
1521         # We should now find the static nbr with a changed mac
1522         #
1523         self.assertFalse(
1524             find_nbr(
1525                 self, self.pg2.sw_if_index, self.pg2.remote_hosts[0].ip4, is_static=0
1526             )
1527         )
1528         self.assertTrue(
1529             find_nbr(
1530                 self,
1531                 self.pg2.sw_if_index,
1532                 self.pg2.remote_hosts[0].ip4,
1533                 is_static=1,
1534                 mac=self.pg2.remote_hosts[1].mac,
1535             )
1536         )
1537
1538         #
1539         # clean-up
1540         #
1541         static_arp.remove_vpp_config()
1542
1543     def test_arp_incomplete(self):
1544         """ARP Incomplete"""
1545         self.pg1.generate_remote_hosts(4)
1546
1547         p0 = (
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)
1551             / Raw()
1552         )
1553         p1 = (
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)
1557             / Raw()
1558         )
1559         p2 = (
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)
1563             / Raw()
1564         )
1565
1566         #
1567         # a packet to an unresolved destination generates an ARP request
1568         #
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
1572         )
1573
1574         #
1575         # add a neighbour for remote host 1
1576         #
1577         static_arp = VppNeighbor(
1578             self,
1579             self.pg1.sw_if_index,
1580             self.pg1.remote_hosts[1].mac,
1581             self.pg1.remote_hosts[1].ip4,
1582             is_static=1,
1583         )
1584         static_arp.add_vpp_config()
1585
1586         #
1587         # add a route through remote host 3 hence we get an incomplete
1588         #
1589         VppIpRoute(
1590             self,
1591             "1.1.1.1",
1592             32,
1593             [VppRoutePath(self.pg1.remote_hosts[3].ip4, self.pg1.sw_if_index)],
1594         ).add_vpp_config()
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
1598         )
1599
1600         #
1601         # change the interface's MAC
1602         #
1603         self.vapi.sw_interface_set_mac_address(
1604             self.pg1.sw_if_index, "00:00:00:33:33:33"
1605         )
1606
1607         #
1608         # now ARP requests come from the new source mac
1609         #
1610         rx = self.send_and_expect(self.pg0, [p1], self.pg1)
1611         self.verify_arp_req(
1612             rx[0],
1613             "00:00:00:33:33:33",
1614             self.pg1.local_ip4,
1615             self.pg1._remote_hosts[2].ip4,
1616         )
1617         rx = self.send_and_expect(self.pg0, [p2], self.pg1)
1618         self.verify_arp_req(
1619             rx[0],
1620             "00:00:00:33:33:33",
1621             self.pg1.local_ip4,
1622             self.pg1._remote_hosts[3].ip4,
1623         )
1624
1625         #
1626         # packets to the resolved host also have the new source mac
1627         #
1628         rx = self.send_and_expect(self.pg0, [p0], self.pg1)
1629         self.verify_ip(
1630             rx[0],
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,
1635         )
1636
1637         #
1638         # set the mac address on the interface that does not have a
1639         # configured subnet and thus no glean
1640         #
1641         self.vapi.sw_interface_set_mac_address(
1642             self.pg2.sw_if_index, "00:00:00:33:33:33"
1643         )
1644
1645     def test_garp(self):
1646         """GARP"""
1647
1648         #
1649         # Generate some hosts on the LAN
1650         #
1651         self.pg1.generate_remote_hosts(4)
1652         self.pg2.generate_remote_hosts(4)
1653
1654         #
1655         # And an ARP entry
1656         #
1657         arp = VppNeighbor(
1658             self,
1659             self.pg1.sw_if_index,
1660             self.pg1.remote_hosts[1].mac,
1661             self.pg1.remote_hosts[1].ip4,
1662         )
1663         arp.add_vpp_config()
1664
1665         self.assertTrue(
1666             find_nbr(
1667                 self,
1668                 self.pg1.sw_if_index,
1669                 self.pg1.remote_hosts[1].ip4,
1670                 mac=self.pg1.remote_hosts[1].mac,
1671             )
1672         )
1673
1674         #
1675         # Send a GARP (request) to swap the host 1's address to that of host 2
1676         #
1677         p1 = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1.remote_hosts[2].mac) / ARP(
1678             op="who-has",
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,
1683         )
1684
1685         self.pg1.add_stream(p1)
1686         self.pg_enable_capture(self.pg_interfaces)
1687         self.pg_start()
1688
1689         self.assertTrue(
1690             find_nbr(
1691                 self,
1692                 self.pg1.sw_if_index,
1693                 self.pg1.remote_hosts[1].ip4,
1694                 mac=self.pg1.remote_hosts[2].mac,
1695             )
1696         )
1697         self.assert_equal(self.get_arp_rx_garp(self.pg1), 1)
1698
1699         #
1700         # Send a GARP (reply) to swap the host 1's address to that of host 3
1701         #
1702         p1 = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1.remote_hosts[3].mac) / ARP(
1703             op="is-at",
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,
1708         )
1709
1710         self.pg1.add_stream(p1)
1711         self.pg_enable_capture(self.pg_interfaces)
1712         self.pg_start()
1713
1714         self.assertTrue(
1715             find_nbr(
1716                 self,
1717                 self.pg1.sw_if_index,
1718                 self.pg1.remote_hosts[1].ip4,
1719                 mac=self.pg1.remote_hosts[3].mac,
1720             )
1721         )
1722         self.assert_equal(self.get_arp_rx_garp(self.pg1), 2)
1723
1724         #
1725         # GARPs (request nor replies) for host we don't know yet
1726         # don't result in new neighbour entries
1727         #
1728         p1 = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1.remote_hosts[3].mac) / ARP(
1729             op="who-has",
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,
1734         )
1735
1736         self.pg1.add_stream(p1)
1737         self.pg_enable_capture(self.pg_interfaces)
1738         self.pg_start()
1739
1740         self.assertFalse(
1741             find_nbr(self, self.pg1.sw_if_index, self.pg1.remote_hosts[2].ip4)
1742         )
1743
1744         p1 = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1.remote_hosts[3].mac) / ARP(
1745             op="is-at",
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,
1750         )
1751
1752         self.pg1.add_stream(p1)
1753         self.pg_enable_capture(self.pg_interfaces)
1754         self.pg_start()
1755
1756         self.assertFalse(
1757             find_nbr(self, self.pg1.sw_if_index, self.pg1.remote_hosts[2].ip4)
1758         )
1759
1760         #
1761         # IP address in different subnets are not learnt
1762         #
1763         self.pg2.configure_ipv4_neighbors()
1764
1765         for op in ["is-at", "who-has"]:
1766             p1 = [
1767                 (
1768                     Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_hosts[1].mac)
1769                     / ARP(
1770                         op=op,
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,
1775                     )
1776                 ),
1777                 (
1778                     Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_hosts[1].mac)
1779                     / ARP(
1780                         op=op,
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,
1785                     )
1786                 ),
1787             ]
1788
1789             self.send_and_assert_no_replies(self.pg1, p1)
1790             self.assertFalse(
1791                 find_nbr(self, self.pg1.sw_if_index, self.pg2.remote_hosts[1].ip4)
1792             )
1793
1794         # they are all dropped because the subnet's don't match
1795         self.assertEqual(
1796             4,
1797             self.statistics.get_err_counter(
1798                 "/err/arp-reply/IP4 destination address not local to subnet"
1799             ),
1800         )
1801
1802     def test_arp_incomplete2(self):
1803         """Incomplete Entries"""
1804
1805         #
1806         # ensure that we throttle the ARP and ND requests
1807         #
1808         self.pg0.generate_remote_hosts(2)
1809
1810         #
1811         # IPv4/ARP
1812         #
1813         ip_10_0_0_1 = VppIpRoute(
1814             self,
1815             "10.0.0.1",
1816             32,
1817             [VppRoutePath(self.pg0.remote_hosts[1].ip4, self.pg0.sw_if_index)],
1818         )
1819         ip_10_0_0_1.add_vpp_config()
1820
1821         p1 = (
1822             Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
1823             / IP(src=self.pg1.remote_ip4, dst="10.0.0.1")
1824             / UDP(sport=1234, dport=1234)
1825             / Raw()
1826         )
1827
1828         self.pg1.add_stream(p1 * 257)
1829         self.pg_enable_capture(self.pg_interfaces)
1830         self.pg_start()
1831         rx = self.pg0._get_capture(1)
1832
1833         #
1834         # how many we get is going to be dependent on the time for packet
1835         # processing but it should be small
1836         #
1837         self.assertLess(len(rx), 64)
1838
1839         #
1840         # IPv6/ND
1841         #
1842         ip_10_1 = VppIpRoute(
1843             self,
1844             "10::1",
1845             128,
1846             [
1847                 VppRoutePath(
1848                     self.pg0.remote_hosts[1].ip6,
1849                     self.pg0.sw_if_index,
1850                     proto=DpoProto.DPO_PROTO_IP6,
1851                 )
1852             ],
1853         )
1854         ip_10_1.add_vpp_config()
1855
1856         p1 = (
1857             Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
1858             / IPv6(src=self.pg1.remote_ip6, dst="10::1")
1859             / UDP(sport=1234, dport=1234)
1860             / Raw()
1861         )
1862
1863         self.pg1.add_stream(p1 * 257)
1864         self.pg_enable_capture(self.pg_interfaces)
1865         self.pg_start()
1866         rx = self.pg0._get_capture(1)
1867
1868         #
1869         # how many we get is going to be dependent on the time for packet
1870         # processing but it should be small
1871         #
1872         self.assertLess(len(rx), 64)
1873
1874     def test_arp_forus(self):
1875         """ARP for for-us"""
1876
1877         #
1878         # Test that VPP responds with ARP requests to addresses that
1879         # are connected and local routes.
1880         # Use one of the 'remote' addresses in the subnet as a local address
1881         # The intention of this route is that it then acts like a secondary
1882         # address added to an interface
1883         #
1884         self.pg0.generate_remote_hosts(2)
1885
1886         forus = VppIpRoute(
1887             self,
1888             self.pg0.remote_hosts[1].ip4,
1889             32,
1890             [
1891                 VppRoutePath(
1892                     "0.0.0.0",
1893                     self.pg0.sw_if_index,
1894                     type=FibPathType.FIB_PATH_TYPE_LOCAL,
1895                 )
1896             ],
1897         )
1898         forus.add_vpp_config()
1899
1900         p = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
1901             op="who-has",
1902             hwdst=self.pg0.local_mac,
1903             hwsrc=self.pg0.remote_mac,
1904             pdst=self.pg0.remote_hosts[1].ip4,
1905             psrc=self.pg0.remote_ip4,
1906         )
1907
1908         rx = self.send_and_expect(self.pg0, [p], self.pg0)
1909
1910         self.verify_arp_resp(
1911             rx[0],
1912             self.pg0.local_mac,
1913             self.pg0.remote_mac,
1914             self.pg0.remote_hosts[1].ip4,
1915             self.pg0.remote_ip4,
1916         )
1917
1918     def test_arp_table_swap(self):
1919         #
1920         # Generate some hosts on the LAN
1921         #
1922         N_NBRS = 4
1923         self.pg1.generate_remote_hosts(N_NBRS)
1924
1925         for n in range(N_NBRS):
1926             # a route thru each neighbour
1927             VppIpRoute(
1928                 self,
1929                 "10.0.0.%d" % n,
1930                 32,
1931                 [VppRoutePath(self.pg1.remote_hosts[n].ip4, self.pg1.sw_if_index)],
1932             ).add_vpp_config()
1933
1934             # resolve each neighbour
1935             p1 = Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / ARP(
1936                 op="is-at",
1937                 hwdst=self.pg1.local_mac,
1938                 hwsrc="00:00:5e:00:01:09",
1939                 pdst=self.pg1.local_ip4,
1940                 psrc=self.pg1.remote_hosts[n].ip4,
1941             )
1942
1943             self.send_and_assert_no_replies(self.pg1, p1, "ARP reply")
1944
1945         self.logger.info(self.vapi.cli("sh ip neighbors"))
1946
1947         #
1948         # swap the table pg1 is in
1949         #
1950         table = VppIpTable(self, 100).add_vpp_config()
1951
1952         self.pg1.unconfig_ip4()
1953         self.pg1.set_table_ip4(100)
1954         self.pg1.config_ip4()
1955
1956         #
1957         # all neighbours are cleared
1958         #
1959         for n in range(N_NBRS):
1960             self.assertFalse(
1961                 find_nbr(self, self.pg1.sw_if_index, self.pg1.remote_hosts[n].ip4)
1962             )
1963
1964         #
1965         # packets to all neighbours generate ARP requests
1966         #
1967         for n in range(N_NBRS):
1968             # a route thru each neighbour
1969             VppIpRoute(
1970                 self,
1971                 "10.0.0.%d" % n,
1972                 32,
1973                 [VppRoutePath(self.pg1.remote_hosts[n].ip4, self.pg1.sw_if_index)],
1974                 table_id=100,
1975             ).add_vpp_config()
1976
1977             p = (
1978                 Ether(src=self.pg1.remote_hosts[n].mac, dst=self.pg1.local_mac)
1979                 / IP(src=self.pg1.remote_hosts[n].ip4, dst="10.0.0.%d" % n)
1980                 / Raw(b"0x5" * 100)
1981             )
1982             rxs = self.send_and_expect(self.pg1, [p], self.pg1)
1983             for rx in rxs:
1984                 self.verify_arp_req(
1985                     rx,
1986                     self.pg1.local_mac,
1987                     self.pg1.local_ip4,
1988                     self.pg1.remote_hosts[n].ip4,
1989                 )
1990
1991         self.pg1.unconfig_ip4()
1992         self.pg1.set_table_ip4(0)
1993
1994     def test_glean_src_select(self):
1995         """Multi Connecteds"""
1996
1997         #
1998         # configure multiple connected subnets on an interface
1999         # and ensure that ARP requests for hosts on those subnets
2000         # pick up the correct source address
2001         #
2002         conn1 = VppIpInterfaceAddress(self, self.pg1, "10.0.0.1", 24).add_vpp_config()
2003         conn2 = VppIpInterfaceAddress(self, self.pg1, "10.0.1.1", 24).add_vpp_config()
2004
2005         p1 = (
2006             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2007             / IP(src=self.pg1.remote_ip4, dst="10.0.0.128")
2008             / Raw(b"0x5" * 100)
2009         )
2010
2011         rxs = self.send_and_expect(self.pg0, [p1], self.pg1)
2012         for rx in rxs:
2013             self.verify_arp_req(rx, self.pg1.local_mac, "10.0.0.1", "10.0.0.128")
2014
2015         p2 = (
2016             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2017             / IP(src=self.pg1.remote_ip4, dst="10.0.1.128")
2018             / Raw(b"0x5" * 100)
2019         )
2020
2021         rxs = self.send_and_expect(self.pg0, [p2], self.pg1)
2022         for rx in rxs:
2023             self.verify_arp_req(rx, self.pg1.local_mac, "10.0.1.1", "10.0.1.128")
2024
2025         #
2026         # add a local address in the same subnet
2027         #  the source addresses are equivalent. VPP happens to
2028         #  choose the last one that was added
2029         conn3 = VppIpInterfaceAddress(self, self.pg1, "10.0.1.2", 24).add_vpp_config()
2030
2031         rxs = self.send_and_expect(self.pg0, [p2], self.pg1)
2032         for rx in rxs:
2033             self.verify_arp_req(rx, self.pg1.local_mac, "10.0.1.2", "10.0.1.128")
2034
2035         #
2036         # remove
2037         #
2038         conn3.remove_vpp_config()
2039         rxs = self.send_and_expect(self.pg0, [p2], self.pg1)
2040         for rx in rxs:
2041             self.verify_arp_req(rx, self.pg1.local_mac, "10.0.1.1", "10.0.1.128")
2042
2043         #
2044         # add back, this time remove the first one
2045         #
2046         conn3 = VppIpInterfaceAddress(self, self.pg1, "10.0.1.2", 24).add_vpp_config()
2047
2048         rxs = self.send_and_expect(self.pg0, [p2], self.pg1)
2049         for rx in rxs:
2050             self.verify_arp_req(rx, self.pg1.local_mac, "10.0.1.2", "10.0.1.128")
2051
2052         conn1.remove_vpp_config()
2053         rxs = self.send_and_expect(self.pg0, [p2], self.pg1)
2054         for rx in rxs:
2055             self.verify_arp_req(rx, self.pg1.local_mac, "10.0.1.2", "10.0.1.128")
2056
2057         # apply a connected prefix to an interface in a different table
2058         VppIpRoute(
2059             self,
2060             "10.0.1.0",
2061             24,
2062             [VppRoutePath("0.0.0.0", self.pg1.sw_if_index)],
2063             table_id=1,
2064         ).add_vpp_config()
2065
2066         rxs = self.send_and_expect(self.pg3, [p2], self.pg1)
2067         for rx in rxs:
2068             self.verify_arp_req(rx, self.pg1.local_mac, "10.0.1.2", "10.0.1.128")
2069
2070         # cleanup
2071         conn3.remove_vpp_config()
2072         conn2.remove_vpp_config()
2073
2074
2075 @tag_fixme_vpp_workers
2076 class NeighborStatsTestCase(VppTestCase):
2077     """ARP/ND Counters"""
2078
2079     @classmethod
2080     def setUpClass(cls):
2081         super(NeighborStatsTestCase, cls).setUpClass()
2082
2083     @classmethod
2084     def tearDownClass(cls):
2085         super(NeighborStatsTestCase, cls).tearDownClass()
2086
2087     def setUp(self):
2088         super(NeighborStatsTestCase, self).setUp()
2089
2090         self.create_pg_interfaces(range(2))
2091
2092         # pg0 configured with ip4 and 6 addresses used for input
2093         # pg1 configured with ip4 and 6 addresses used for output
2094         # pg2 is unnumbered to pg0
2095         for i in self.pg_interfaces:
2096             i.admin_up()
2097             i.config_ip4()
2098             i.config_ip6()
2099             i.resolve_arp()
2100             i.resolve_ndp()
2101
2102     def tearDown(self):
2103         super(NeighborStatsTestCase, self).tearDown()
2104
2105         for i in self.pg_interfaces:
2106             i.unconfig_ip4()
2107             i.unconfig_ip6()
2108             i.admin_down()
2109
2110     def test_arp_stats(self):
2111         """ARP Counters"""
2112
2113         self.vapi.cli("adj counters enable")
2114         self.pg1.generate_remote_hosts(2)
2115
2116         arp1 = VppNeighbor(
2117             self,
2118             self.pg1.sw_if_index,
2119             self.pg1.remote_hosts[0].mac,
2120             self.pg1.remote_hosts[0].ip4,
2121         )
2122         arp1.add_vpp_config()
2123         arp2 = VppNeighbor(
2124             self,
2125             self.pg1.sw_if_index,
2126             self.pg1.remote_hosts[1].mac,
2127             self.pg1.remote_hosts[1].ip4,
2128         )
2129         arp2.add_vpp_config()
2130
2131         p1 = (
2132             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2133             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_hosts[0].ip4)
2134             / UDP(sport=1234, dport=1234)
2135             / Raw()
2136         )
2137         p2 = (
2138             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2139             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_hosts[1].ip4)
2140             / UDP(sport=1234, dport=1234)
2141             / Raw()
2142         )
2143
2144         rx = self.send_and_expect(self.pg0, p1 * NUM_PKTS, self.pg1)
2145         rx = self.send_and_expect(self.pg0, p2 * NUM_PKTS, self.pg1)
2146
2147         self.assertEqual(NUM_PKTS, arp1.get_stats()["packets"])
2148         self.assertEqual(NUM_PKTS, arp2.get_stats()["packets"])
2149
2150         rx = self.send_and_expect(self.pg0, p1 * NUM_PKTS, self.pg1)
2151         self.assertEqual(NUM_PKTS * 2, arp1.get_stats()["packets"])
2152
2153     def test_nd_stats(self):
2154         """ND Counters"""
2155
2156         self.vapi.cli("adj counters enable")
2157         self.pg0.generate_remote_hosts(3)
2158
2159         nd1 = VppNeighbor(
2160             self,
2161             self.pg0.sw_if_index,
2162             self.pg0.remote_hosts[1].mac,
2163             self.pg0.remote_hosts[1].ip6,
2164         )
2165         nd1.add_vpp_config()
2166         nd2 = VppNeighbor(
2167             self,
2168             self.pg0.sw_if_index,
2169             self.pg0.remote_hosts[2].mac,
2170             self.pg0.remote_hosts[2].ip6,
2171         )
2172         nd2.add_vpp_config()
2173
2174         p1 = (
2175             Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
2176             / IPv6(src=self.pg1.remote_ip6, dst=self.pg0.remote_hosts[1].ip6)
2177             / UDP(sport=1234, dport=1234)
2178             / Raw()
2179         )
2180         p2 = (
2181             Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
2182             / IPv6(src=self.pg1.remote_ip6, dst=self.pg0.remote_hosts[2].ip6)
2183             / UDP(sport=1234, dport=1234)
2184             / Raw()
2185         )
2186
2187         rx = self.send_and_expect(self.pg1, p1 * 16, self.pg0)
2188         rx = self.send_and_expect(self.pg1, p2 * 16, self.pg0)
2189
2190         self.assertEqual(16, nd1.get_stats()["packets"])
2191         self.assertEqual(16, nd2.get_stats()["packets"])
2192
2193         rx = self.send_and_expect(self.pg1, p1 * NUM_PKTS, self.pg0)
2194         self.assertEqual(NUM_PKTS + 16, nd1.get_stats()["packets"])
2195
2196
2197 class NeighborAgeTestCase(VppTestCase):
2198     """ARP/ND Aging"""
2199
2200     @classmethod
2201     def setUpClass(cls):
2202         super(NeighborAgeTestCase, cls).setUpClass()
2203
2204     @classmethod
2205     def tearDownClass(cls):
2206         super(NeighborAgeTestCase, cls).tearDownClass()
2207
2208     def setUp(self):
2209         super(NeighborAgeTestCase, self).setUp()
2210
2211         self.create_pg_interfaces(range(1))
2212
2213         # pg0 configured with ip4 and 6 addresses used for input
2214         # pg1 configured with ip4 and 6 addresses used for output
2215         # pg2 is unnumbered to pg0
2216         for i in self.pg_interfaces:
2217             i.admin_up()
2218             i.config_ip4()
2219             i.config_ip6()
2220             i.resolve_arp()
2221             i.resolve_ndp()
2222
2223     def tearDown(self):
2224         super(NeighborAgeTestCase, self).tearDown()
2225
2226         for i in self.pg_interfaces:
2227             i.unconfig_ip4()
2228             i.unconfig_ip6()
2229             i.admin_down()
2230
2231     def verify_arp_req(self, rx, smac, sip, dip):
2232         ether = rx[Ether]
2233         self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
2234         self.assertEqual(ether.src, smac)
2235
2236         arp = rx[ARP]
2237         self.assertEqual(arp.hwtype, 1)
2238         self.assertEqual(arp.ptype, 0x800)
2239         self.assertEqual(arp.hwlen, 6)
2240         self.assertEqual(arp.plen, 4)
2241         self.assertEqual(arp.op, arp_opts["who-has"])
2242         self.assertEqual(arp.hwsrc, smac)
2243         self.assertEqual(arp.hwdst, "00:00:00:00:00:00")
2244         self.assertEqual(arp.psrc, sip)
2245         self.assertEqual(arp.pdst, dip)
2246
2247     def test_age(self):
2248         """Aging/Recycle"""
2249
2250         self.vapi.cli("set logging unthrottle 0")
2251         self.vapi.cli("set logging size %d" % 0xFFFF)
2252
2253         self.pg0.generate_remote_hosts(201)
2254
2255         vaf = VppEnum.vl_api_address_family_t
2256
2257         #
2258         # start listening on all interfaces
2259         #
2260         self.pg_enable_capture(self.pg_interfaces)
2261
2262         #
2263         # Set the neighbor configuration:
2264         #   limi = 200
2265         #   age  = 0 seconds
2266         #   recycle = false
2267         #
2268         self.vapi.ip_neighbor_config(
2269             af=vaf.ADDRESS_IP4, max_number=200, max_age=0, recycle=False
2270         )
2271
2272         self.vapi.cli("sh ip neighbor-config")
2273
2274         # add the 198 neighbours that should pass (-1 for one created in setup)
2275         for ii in range(200):
2276             VppNeighbor(
2277                 self,
2278                 self.pg0.sw_if_index,
2279                 self.pg0.remote_hosts[ii].mac,
2280                 self.pg0.remote_hosts[ii].ip4,
2281             ).add_vpp_config()
2282
2283         # one more neighbor over the limit should fail
2284         with self.vapi.assert_negative_api_retval():
2285             VppNeighbor(
2286                 self,
2287                 self.pg0.sw_if_index,
2288                 self.pg0.remote_hosts[200].mac,
2289                 self.pg0.remote_hosts[200].ip4,
2290             ).add_vpp_config()
2291
2292         #
2293         # change the config to allow recycling the old neighbors
2294         #
2295         self.vapi.ip_neighbor_config(
2296             af=vaf.ADDRESS_IP4, max_number=200, max_age=0, recycle=True
2297         )
2298
2299         # now new additions are allowed
2300         VppNeighbor(
2301             self,
2302             self.pg0.sw_if_index,
2303             self.pg0.remote_hosts[200].mac,
2304             self.pg0.remote_hosts[200].ip4,
2305         ).add_vpp_config()
2306
2307         # add the first neighbor we configured has been re-used
2308         self.assertFalse(
2309             find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[0].ip4)
2310         )
2311         self.assertTrue(
2312             find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[200].ip4)
2313         )
2314
2315         #
2316         # change the config to age old neighbors
2317         #
2318         self.vapi.ip_neighbor_config(
2319             af=vaf.ADDRESS_IP4, max_number=200, max_age=2, recycle=True
2320         )
2321
2322         self.vapi.cli("sh ip4 neighbor-sorted")
2323
2324         # age out neighbors
2325         self.virtual_sleep(3)
2326
2327         #
2328         # expect probes from all these ARP entries as they age
2329         # 3 probes for each neighbor 3*200 = 600
2330         rxs = self.pg0.get_capture(600, timeout=2)
2331
2332         for ii in range(3):
2333             for jj in range(200):
2334                 rx = rxs[ii * 200 + jj]
2335                 # rx.show()
2336
2337         #
2338         # 3 probes sent then 1 more second to see if a reply comes, before
2339         # they age out
2340         #
2341         self.virtual_sleep(1)
2342
2343         self.assertFalse(
2344             self.vapi.ip_neighbor_dump(sw_if_index=0xFFFFFFFF, af=vaf.ADDRESS_IP4)
2345         )
2346
2347         #
2348         # load up some neighbours again with 2s aging enabled
2349         # they should be removed after 10s (2s age + 4s for probes + gap)
2350         # check for the add and remove events
2351         #
2352         enum = VppEnum.vl_api_ip_neighbor_event_flags_t
2353
2354         self.vapi.want_ip_neighbor_events_v2(enable=1)
2355         for ii in range(10):
2356             VppNeighbor(
2357                 self,
2358                 self.pg0.sw_if_index,
2359                 self.pg0.remote_hosts[ii].mac,
2360                 self.pg0.remote_hosts[ii].ip4,
2361             ).add_vpp_config()
2362
2363             e = self.vapi.wait_for_event(1, "ip_neighbor_event_v2")
2364             self.assertEqual(e.flags, enum.IP_NEIGHBOR_API_EVENT_FLAG_ADDED)
2365             self.assertEqual(str(e.neighbor.ip_address), self.pg0.remote_hosts[ii].ip4)
2366             self.assertEqual(e.neighbor.mac_address, self.pg0.remote_hosts[ii].mac)
2367
2368         self.virtual_sleep(10)
2369         self.assertFalse(
2370             self.vapi.ip_neighbor_dump(sw_if_index=0xFFFFFFFF, af=vaf.ADDRESS_IP4)
2371         )
2372
2373         evs = []
2374         for ii in range(10):
2375             e = self.vapi.wait_for_event(1, "ip_neighbor_event_v2")
2376             self.assertEqual(e.flags, enum.IP_NEIGHBOR_API_EVENT_FLAG_REMOVED)
2377             evs.append(e)
2378
2379         # check we got the correct mac/ip pairs - done separately
2380         # because we don't care about the order the remove notifications
2381         # arrive
2382         for ii in range(10):
2383             found = False
2384             mac = self.pg0.remote_hosts[ii].mac
2385             ip = self.pg0.remote_hosts[ii].ip4
2386
2387             for e in evs:
2388                 if e.neighbor.mac_address == mac and str(e.neighbor.ip_address) == ip:
2389                     found = True
2390                     break
2391             self.assertTrue(found)
2392
2393         #
2394         # check if we can set age and recycle with empty neighbor list
2395         #
2396         self.vapi.ip_neighbor_config(
2397             af=vaf.ADDRESS_IP4, max_number=200, max_age=1000, recycle=True
2398         )
2399
2400         #
2401         # load up some neighbours again, then disable the aging
2402         # they should still be there in 10 seconds time
2403         #
2404         for ii in range(10):
2405             VppNeighbor(
2406                 self,
2407                 self.pg0.sw_if_index,
2408                 self.pg0.remote_hosts[ii].mac,
2409                 self.pg0.remote_hosts[ii].ip4,
2410             ).add_vpp_config()
2411         self.vapi.ip_neighbor_config(
2412             af=vaf.ADDRESS_IP4, max_number=200, max_age=0, recycle=False
2413         )
2414
2415         self.virtual_sleep(10)
2416         self.assertTrue(
2417             find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[0].ip4)
2418         )
2419
2420
2421 class NeighborReplaceTestCase(VppTestCase):
2422     """ARP/ND Replacement"""
2423
2424     @classmethod
2425     def setUpClass(cls):
2426         super(NeighborReplaceTestCase, cls).setUpClass()
2427
2428     @classmethod
2429     def tearDownClass(cls):
2430         super(NeighborReplaceTestCase, cls).tearDownClass()
2431
2432     def setUp(self):
2433         super(NeighborReplaceTestCase, self).setUp()
2434
2435         self.create_pg_interfaces(range(4))
2436
2437         # pg0 configured with ip4 and 6 addresses used for input
2438         # pg1 configured with ip4 and 6 addresses used for output
2439         # pg2 is unnumbered to pg0
2440         for i in self.pg_interfaces:
2441             i.admin_up()
2442             i.config_ip4()
2443             i.config_ip6()
2444             i.resolve_arp()
2445             i.resolve_ndp()
2446
2447     def tearDown(self):
2448         super(NeighborReplaceTestCase, self).tearDown()
2449
2450         for i in self.pg_interfaces:
2451             i.unconfig_ip4()
2452             i.unconfig_ip6()
2453             i.admin_down()
2454
2455     def test_replace(self):
2456         """replace"""
2457
2458         N_HOSTS = 16
2459
2460         for i in self.pg_interfaces:
2461             i.generate_remote_hosts(N_HOSTS)
2462             i.configure_ipv4_neighbors()
2463             i.configure_ipv6_neighbors()
2464
2465         # replace them all
2466         self.vapi.ip_neighbor_replace_begin()
2467         self.vapi.ip_neighbor_replace_end()
2468
2469         for i in self.pg_interfaces:
2470             for h in range(N_HOSTS):
2471                 self.assertFalse(
2472                     find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[h].ip4)
2473                 )
2474                 self.assertFalse(
2475                     find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[h].ip6)
2476                 )
2477
2478         #
2479         # and them all back via the API
2480         #
2481         for i in self.pg_interfaces:
2482             for h in range(N_HOSTS):
2483                 VppNeighbor(
2484                     self, i.sw_if_index, i.remote_hosts[h].mac, i.remote_hosts[h].ip4
2485                 ).add_vpp_config()
2486                 VppNeighbor(
2487                     self, i.sw_if_index, i.remote_hosts[h].mac, i.remote_hosts[h].ip6
2488                 ).add_vpp_config()
2489
2490         #
2491         # begin the replacement again, this time touch some
2492         # the neighbours on pg1 so they are not deleted
2493         #
2494         self.vapi.ip_neighbor_replace_begin()
2495
2496         # update from the API all neighbours on pg1
2497         for h in range(N_HOSTS):
2498             VppNeighbor(
2499                 self,
2500                 self.pg1.sw_if_index,
2501                 self.pg1.remote_hosts[h].mac,
2502                 self.pg1.remote_hosts[h].ip4,
2503             ).add_vpp_config()
2504             VppNeighbor(
2505                 self,
2506                 self.pg1.sw_if_index,
2507                 self.pg1.remote_hosts[h].mac,
2508                 self.pg1.remote_hosts[h].ip6,
2509             ).add_vpp_config()
2510
2511         # update from the data-plane all neighbours on pg3
2512         self.pg3.configure_ipv4_neighbors()
2513         self.pg3.configure_ipv6_neighbors()
2514
2515         # complete the replacement
2516         self.logger.info(self.vapi.cli("sh ip neighbors"))
2517         self.vapi.ip_neighbor_replace_end()
2518
2519         for i in self.pg_interfaces:
2520             if i == self.pg1 or i == self.pg3:
2521                 # neighbours on pg1 and pg3 are still present
2522                 for h in range(N_HOSTS):
2523                     self.assertTrue(
2524                         find_nbr(self, i.sw_if_index, i.remote_hosts[h].ip4)
2525                     )
2526                     self.assertTrue(
2527                         find_nbr(self, i.sw_if_index, i.remote_hosts[h].ip6)
2528                     )
2529             else:
2530                 # all other neighbours are toast
2531                 for h in range(N_HOSTS):
2532                     self.assertFalse(
2533                         find_nbr(self, i.sw_if_index, i.remote_hosts[h].ip4)
2534                     )
2535                     self.assertFalse(
2536                         find_nbr(self, i.sw_if_index, i.remote_hosts[h].ip6)
2537                     )
2538
2539
2540 class NeighborFlush(VppTestCase):
2541     """Neighbor Flush"""
2542
2543     @classmethod
2544     def setUpClass(cls):
2545         super(NeighborFlush, cls).setUpClass()
2546
2547     @classmethod
2548     def tearDownClass(cls):
2549         super(NeighborFlush, cls).tearDownClass()
2550
2551     def setUp(self):
2552         super(NeighborFlush, self).setUp()
2553
2554         self.create_pg_interfaces(range(2))
2555
2556         for i in self.pg_interfaces:
2557             i.admin_up()
2558             i.config_ip4()
2559             i.config_ip6()
2560             i.resolve_arp()
2561             i.resolve_ndp()
2562
2563     def tearDown(self):
2564         super(NeighborFlush, self).tearDown()
2565
2566         for i in self.pg_interfaces:
2567             i.unconfig_ip4()
2568             i.unconfig_ip6()
2569             i.admin_down()
2570
2571     def test_flush(self):
2572         """Neighbour Flush"""
2573
2574         e = VppEnum
2575         nf = e.vl_api_ip_neighbor_flags_t
2576         af = e.vl_api_address_family_t
2577         N_HOSTS = 16
2578         static = [False, True]
2579         self.pg0.generate_remote_hosts(N_HOSTS)
2580         self.pg1.generate_remote_hosts(N_HOSTS)
2581
2582         for s in static:
2583             # a few v4 and v6 dynamic neoghbors
2584             for n in range(N_HOSTS):
2585                 VppNeighbor(
2586                     self,
2587                     self.pg0.sw_if_index,
2588                     self.pg0.remote_hosts[n].mac,
2589                     self.pg0.remote_hosts[n].ip4,
2590                     is_static=s,
2591                 ).add_vpp_config()
2592                 VppNeighbor(
2593                     self,
2594                     self.pg1.sw_if_index,
2595                     self.pg1.remote_hosts[n].mac,
2596                     self.pg1.remote_hosts[n].ip6,
2597                     is_static=s,
2598                 ).add_vpp_config()
2599
2600             # flush the interfaces individually
2601             self.vapi.ip_neighbor_flush(af.ADDRESS_IP4, self.pg0.sw_if_index)
2602
2603             # check we haven't flushed that which we shouldn't
2604             for n in range(N_HOSTS):
2605                 self.assertTrue(
2606                     find_nbr(
2607                         self,
2608                         self.pg1.sw_if_index,
2609                         self.pg1.remote_hosts[n].ip6,
2610                         is_static=s,
2611                     )
2612                 )
2613
2614             self.vapi.ip_neighbor_flush(af.ADDRESS_IP6, self.pg1.sw_if_index)
2615
2616             for n in range(N_HOSTS):
2617                 self.assertFalse(
2618                     find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[n].ip4)
2619                 )
2620                 self.assertFalse(
2621                     find_nbr(self, self.pg1.sw_if_index, self.pg1.remote_hosts[n].ip6)
2622                 )
2623
2624             # add the nieghbours back
2625             for n in range(N_HOSTS):
2626                 VppNeighbor(
2627                     self,
2628                     self.pg0.sw_if_index,
2629                     self.pg0.remote_hosts[n].mac,
2630                     self.pg0.remote_hosts[n].ip4,
2631                     is_static=s,
2632                 ).add_vpp_config()
2633                 VppNeighbor(
2634                     self,
2635                     self.pg1.sw_if_index,
2636                     self.pg1.remote_hosts[n].mac,
2637                     self.pg1.remote_hosts[n].ip6,
2638                     is_static=s,
2639                 ).add_vpp_config()
2640
2641             self.logger.info(self.vapi.cli("sh ip neighbor"))
2642
2643             # flush both interfaces at the same time
2644             self.vapi.ip_neighbor_flush(af.ADDRESS_IP6, 0xFFFFFFFF)
2645
2646             # check we haven't flushed that which we shouldn't
2647             for n in range(N_HOSTS):
2648                 self.assertTrue(
2649                     find_nbr(
2650                         self,
2651                         self.pg0.sw_if_index,
2652                         self.pg0.remote_hosts[n].ip4,
2653                         is_static=s,
2654                     )
2655                 )
2656
2657             self.vapi.ip_neighbor_flush(af.ADDRESS_IP4, 0xFFFFFFFF)
2658
2659             for n in range(N_HOSTS):
2660                 self.assertFalse(
2661                     find_nbr(self, self.pg0.sw_if_index, self.pg0.remote_hosts[n].ip4)
2662                 )
2663                 self.assertFalse(
2664                     find_nbr(self, self.pg1.sw_if_index, self.pg1.remote_hosts[n].ip6)
2665                 )
2666
2667
2668 if __name__ == "__main__":
2669     unittest.main(testRunner=VppTestRunner)