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