4 from socket import AF_INET, AF_INET6, inet_pton
6 from framework import VppTestCase, VppTestRunner
7 from vpp_neighbor import VppNeighbor, find_nbr
8 from vpp_ip_route import VppIpRoute, VppRoutePath, find_route
10 from scapy.packet import Raw
11 from scapy.layers.l2 import Ether, ARP
12 from scapy.layers.inet import IP, UDP
13 from scapy.contrib.mpls import MPLS
15 # not exported by scapy, so redefined here
16 arp_opts = {"who-has": 1, "is-at": 2}
19 class ARPTestCase(VppTestCase):
23 super(ARPTestCase, self).setUp()
25 # create 3 pg interfaces
26 self.create_pg_interfaces(range(4))
28 # pg0 configured with ip4 and 6 addresses used for input
29 # pg1 configured with ip4 and 6 addresses used for output
30 # pg2 is unnumbered to pg0
31 for i in self.pg_interfaces:
36 self.pg0.resolve_arp()
41 # pg3 in a different VRF
42 self.pg3.set_table_ip4(1)
46 super(ARPTestCase, self).tearDown()
47 self.pg0.unconfig_ip4()
48 self.pg0.unconfig_ip6()
50 self.pg1.unconfig_ip4()
51 self.pg1.unconfig_ip6()
53 self.pg3.unconfig_ip4()
55 for i in self.pg_interfaces:
58 def verify_arp_req(self, rx, smac, sip, dip):
60 self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
61 self.assertEqual(ether.src, smac)
64 self.assertEqual(arp.hwtype, 1)
65 self.assertEqual(arp.ptype, 0x800)
66 self.assertEqual(arp.hwlen, 6)
67 self.assertEqual(arp.plen, 4)
68 self.assertEqual(arp.op, arp_opts["who-has"])
69 self.assertEqual(arp.hwsrc, smac)
70 self.assertEqual(arp.hwdst, "00:00:00:00:00:00")
71 self.assertEqual(arp.psrc, sip)
72 self.assertEqual(arp.pdst, dip)
74 def verify_arp_resp(self, rx, smac, dmac, sip, dip):
76 self.assertEqual(ether.dst, dmac)
77 self.assertEqual(ether.src, smac)
80 self.assertEqual(arp.hwtype, 1)
81 self.assertEqual(arp.ptype, 0x800)
82 self.assertEqual(arp.hwlen, 6)
83 self.assertEqual(arp.plen, 4)
84 self.assertEqual(arp.op, arp_opts["is-at"])
85 self.assertEqual(arp.hwsrc, smac)
86 self.assertEqual(arp.hwdst, dmac)
87 self.assertEqual(arp.psrc, sip)
88 self.assertEqual(arp.pdst, dip)
90 def verify_ip(self, rx, smac, dmac, sip, dip):
92 self.assertEqual(ether.dst, dmac)
93 self.assertEqual(ether.src, smac)
96 self.assertEqual(ip.src, sip)
97 self.assertEqual(ip.dst, dip)
99 def verify_ip_o_mpls(self, rx, smac, dmac, label, sip, dip):
101 self.assertEqual(ether.dst, dmac)
102 self.assertEqual(ether.src, smac)
105 self.assertTrue(mpls.label, label)
108 self.assertEqual(ip.src, sip)
109 self.assertEqual(ip.dst, dip)
111 def send_and_assert_no_replies(self, intf, pkts, remark):
112 intf.add_stream(pkts)
113 self.pg_enable_capture(self.pg_interfaces)
115 for i in self.pg_interfaces:
116 i.assert_nothing_captured(remark=remark)
122 # Generate some hosts on the LAN
124 self.pg1.generate_remote_hosts(9)
127 # Send IP traffic to one of these unresolved hosts.
128 # expect the generation of an ARP request
130 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
131 IP(src=self.pg0.remote_ip4, dst=self.pg1._remote_hosts[1].ip4) /
132 UDP(sport=1234, dport=1234) /
135 self.pg0.add_stream(p)
136 self.pg_enable_capture(self.pg_interfaces)
139 rx = self.pg1.get_capture(1)
141 self.verify_arp_req(rx[0],
144 self.pg1._remote_hosts[1].ip4)
147 # And a dynamic ARP entry for host 1
149 dyn_arp = VppNeighbor(self,
150 self.pg1.sw_if_index,
151 self.pg1.remote_hosts[1].mac,
152 self.pg1.remote_hosts[1].ip4)
153 dyn_arp.add_vpp_config()
156 # now we expect IP traffic forwarded
158 dyn_p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
159 IP(src=self.pg0.remote_ip4,
160 dst=self.pg1._remote_hosts[1].ip4) /
161 UDP(sport=1234, dport=1234) /
164 self.pg0.add_stream(dyn_p)
165 self.pg_enable_capture(self.pg_interfaces)
168 rx = self.pg1.get_capture(1)
170 self.verify_ip(rx[0],
172 self.pg1.remote_hosts[1].mac,
174 self.pg1._remote_hosts[1].ip4)
177 # And a Static ARP entry for host 2
179 static_arp = VppNeighbor(self,
180 self.pg1.sw_if_index,
181 self.pg1.remote_hosts[2].mac,
182 self.pg1.remote_hosts[2].ip4,
184 static_arp.add_vpp_config()
186 static_p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
187 IP(src=self.pg0.remote_ip4,
188 dst=self.pg1._remote_hosts[2].ip4) /
189 UDP(sport=1234, dport=1234) /
192 self.pg0.add_stream(static_p)
193 self.pg_enable_capture(self.pg_interfaces)
196 rx = self.pg1.get_capture(1)
198 self.verify_ip(rx[0],
200 self.pg1.remote_hosts[2].mac,
202 self.pg1._remote_hosts[2].ip4)
205 # flap the link. dynamic ARPs get flush, statics don't
207 self.pg1.admin_down()
210 self.pg0.add_stream(static_p)
211 self.pg_enable_capture(self.pg_interfaces)
213 rx = self.pg1.get_capture(1)
215 self.verify_ip(rx[0],
217 self.pg1.remote_hosts[2].mac,
219 self.pg1._remote_hosts[2].ip4)
221 self.pg0.add_stream(dyn_p)
222 self.pg_enable_capture(self.pg_interfaces)
225 rx = self.pg1.get_capture(1)
226 self.verify_arp_req(rx[0],
229 self.pg1._remote_hosts[1].ip4)
232 # Send an ARP request from one of the so-far unlearned remote hosts
234 p = (Ether(dst="ff:ff:ff:ff:ff:ff",
235 src=self.pg1._remote_hosts[3].mac) /
237 hwsrc=self.pg1._remote_hosts[3].mac,
238 pdst=self.pg1.local_ip4,
239 psrc=self.pg1._remote_hosts[3].ip4))
241 self.pg1.add_stream(p)
242 self.pg_enable_capture(self.pg_interfaces)
245 rx = self.pg1.get_capture(1)
246 self.verify_arp_resp(rx[0],
248 self.pg1._remote_hosts[3].mac,
250 self.pg1._remote_hosts[3].ip4)
253 # VPP should have learned the mapping for the remote host
255 self.assertTrue(find_nbr(self,
256 self.pg1.sw_if_index,
257 self.pg1._remote_hosts[3].ip4))
259 # Fire in an ARP request before the interface becomes IP enabled
261 self.pg2.generate_remote_hosts(4)
263 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
265 hwsrc=self.pg2.remote_mac,
266 pdst=self.pg1.local_ip4,
267 psrc=self.pg2.remote_hosts[3].ip4))
268 self.send_and_assert_no_replies(self.pg2, p,
269 "interface not IP enabled")
272 # Make pg2 un-numbered to pg1
274 self.pg2.set_unnumbered(self.pg1.sw_if_index)
277 # We should respond to ARP requests for the unnumbered to address
278 # once an attached route to the source is known
280 self.send_and_assert_no_replies(
282 "ARP req for unnumbered address - no source")
284 attached_host = VppIpRoute(self, self.pg2.remote_hosts[3].ip4, 32,
285 [VppRoutePath("0.0.0.0",
286 self.pg2.sw_if_index)])
287 attached_host.add_vpp_config()
289 self.pg2.add_stream(p)
290 self.pg_enable_capture(self.pg_interfaces)
293 rx = self.pg2.get_capture(1)
294 self.verify_arp_resp(rx[0],
298 self.pg2.remote_hosts[3].ip4)
301 # A neighbor entry that has no associated FIB-entry
303 arp_no_fib = VppNeighbor(self,
304 self.pg1.sw_if_index,
305 self.pg1.remote_hosts[4].mac,
306 self.pg1.remote_hosts[4].ip4,
308 arp_no_fib.add_vpp_config()
311 # check we have the neighbor, but no route
313 self.assertTrue(find_nbr(self,
314 self.pg1.sw_if_index,
315 self.pg1._remote_hosts[4].ip4))
316 self.assertFalse(find_route(self,
317 self.pg1._remote_hosts[4].ip4,
320 # pg2 is unnumbered to pg1, so we can form adjacencies out of pg2
321 # from within pg1's subnet
323 arp_unnum = VppNeighbor(self,
324 self.pg2.sw_if_index,
325 self.pg1.remote_hosts[5].mac,
326 self.pg1.remote_hosts[5].ip4)
327 arp_unnum.add_vpp_config()
329 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
330 IP(src=self.pg0.remote_ip4,
331 dst=self.pg1._remote_hosts[5].ip4) /
332 UDP(sport=1234, dport=1234) /
335 self.pg0.add_stream(p)
336 self.pg_enable_capture(self.pg_interfaces)
339 rx = self.pg2.get_capture(1)
341 self.verify_ip(rx[0],
343 self.pg1.remote_hosts[5].mac,
345 self.pg1._remote_hosts[5].ip4)
348 # ARP requests from hosts in pg1's subnet sent on pg2 are replied to
349 # with the unnumbered interface's address as the source
351 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
353 hwsrc=self.pg2.remote_mac,
354 pdst=self.pg1.local_ip4,
355 psrc=self.pg1.remote_hosts[6].ip4))
357 self.pg2.add_stream(p)
358 self.pg_enable_capture(self.pg_interfaces)
361 rx = self.pg2.get_capture(1)
362 self.verify_arp_resp(rx[0],
366 self.pg1.remote_hosts[6].ip4)
369 # An attached host route out of pg2 for an undiscovered hosts generates
370 # an ARP request with the unnumbered address as the source
372 att_unnum = VppIpRoute(self, self.pg1.remote_hosts[7].ip4, 32,
373 [VppRoutePath("0.0.0.0",
374 self.pg2.sw_if_index)])
375 att_unnum.add_vpp_config()
377 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
378 IP(src=self.pg0.remote_ip4,
379 dst=self.pg1._remote_hosts[7].ip4) /
380 UDP(sport=1234, dport=1234) /
383 self.pg0.add_stream(p)
384 self.pg_enable_capture(self.pg_interfaces)
387 rx = self.pg2.get_capture(1)
389 self.verify_arp_req(rx[0],
392 self.pg1._remote_hosts[7].ip4)
394 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
396 hwsrc=self.pg2.remote_mac,
397 pdst=self.pg1.local_ip4,
398 psrc=self.pg1.remote_hosts[7].ip4))
400 self.pg2.add_stream(p)
401 self.pg_enable_capture(self.pg_interfaces)
404 rx = self.pg2.get_capture(1)
405 self.verify_arp_resp(rx[0],
409 self.pg1.remote_hosts[7].ip4)
412 # An attached host route as yet unresolved out of pg2 for an
413 # undiscovered host, an ARP requests begets a response.
415 att_unnum1 = VppIpRoute(self, self.pg1.remote_hosts[8].ip4, 32,
416 [VppRoutePath("0.0.0.0",
417 self.pg2.sw_if_index)])
418 att_unnum1.add_vpp_config()
420 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
422 hwsrc=self.pg2.remote_mac,
423 pdst=self.pg1.local_ip4,
424 psrc=self.pg1.remote_hosts[8].ip4))
426 self.pg2.add_stream(p)
427 self.pg_enable_capture(self.pg_interfaces)
430 rx = self.pg2.get_capture(1)
431 self.verify_arp_resp(rx[0],
435 self.pg1.remote_hosts[8].ip4)
439 # 1 - don't respond to ARP request for address not within the
440 # interface's sub-net
441 # 1a - nor within the unnumbered subnet
442 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
444 hwsrc=self.pg0.remote_mac,
446 psrc=self.pg0.remote_ip4))
447 self.send_and_assert_no_replies(self.pg0, p,
448 "ARP req for non-local destination")
449 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
451 hwsrc=self.pg2.remote_mac,
453 psrc=self.pg1.remote_hosts[7].ip4))
454 self.send_and_assert_no_replies(
456 "ARP req for non-local destination - unnum")
459 # 2 - don't respond to ARP request from an address not within the
460 # interface's sub-net
462 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
464 hwsrc=self.pg0.remote_mac,
466 pdst=self.pg0.local_ip4))
467 self.send_and_assert_no_replies(self.pg0, p,
468 "ARP req for non-local source")
469 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
471 hwsrc=self.pg2.remote_mac,
473 pdst=self.pg0.local_ip4))
474 self.send_and_assert_no_replies(
476 "ARP req for non-local source - unnum")
479 # 3 - don't respond to ARP request from an address that belongs to
482 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
484 hwsrc=self.pg0.remote_mac,
485 psrc=self.pg0.local_ip4,
486 pdst=self.pg0.local_ip4))
487 self.send_and_assert_no_replies(self.pg0, p,
488 "ARP req for non-local source")
491 # 4 - don't respond to ARP requests that has mac source different
492 # from ARP request HW source
495 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
497 hwsrc="00:00:00:DE:AD:BE",
498 psrc=self.pg0.remote_ip4,
499 pdst=self.pg0.local_ip4))
500 self.send_and_assert_no_replies(self.pg0, p,
501 "ARP req for non-local source")
506 dyn_arp.remove_vpp_config()
507 static_arp.remove_vpp_config()
508 self.pg2.unset_unnumbered(self.pg1.sw_if_index)
510 # need this to flush the adj-fibs
511 self.pg2.unset_unnumbered(self.pg1.sw_if_index)
512 self.pg2.admin_down()
514 def test_proxy_arp(self):
518 # Proxy ARP rewquest packets for each interface
520 arp_req_pg2 = (Ether(src=self.pg2.remote_mac,
521 dst="ff:ff:ff:ff:ff:ff") /
523 hwsrc=self.pg2.remote_mac,
525 psrc=self.pg1.remote_ip4))
526 arp_req_pg0 = (Ether(src=self.pg0.remote_mac,
527 dst="ff:ff:ff:ff:ff:ff") /
529 hwsrc=self.pg0.remote_mac,
531 psrc=self.pg0.remote_ip4))
532 arp_req_pg1 = (Ether(src=self.pg1.remote_mac,
533 dst="ff:ff:ff:ff:ff:ff") /
535 hwsrc=self.pg1.remote_mac,
537 psrc=self.pg1.remote_ip4))
538 arp_req_pg3 = (Ether(src=self.pg3.remote_mac,
539 dst="ff:ff:ff:ff:ff:ff") /
541 hwsrc=self.pg3.remote_mac,
543 psrc=self.pg3.remote_ip4))
546 # Configure Proxy ARP for 10.10.10.0 -> 10.10.10.124
548 self.vapi.proxy_arp_add_del(inet_pton(AF_INET, "10.10.10.2"),
549 inet_pton(AF_INET, "10.10.10.124"))
552 # No responses are sent when the interfaces are not enabled for proxy
555 self.send_and_assert_no_replies(self.pg0, arp_req_pg0,
556 "ARP req from unconfigured interface")
557 self.send_and_assert_no_replies(self.pg2, arp_req_pg2,
558 "ARP req from unconfigured interface")
561 # Make pg2 un-numbered to pg1
564 self.pg2.set_unnumbered(self.pg1.sw_if_index)
566 self.send_and_assert_no_replies(self.pg2, arp_req_pg2,
567 "ARP req from unnumbered interface")
570 # Enable each interface to reply to proxy ARPs
572 for i in self.pg_interfaces:
576 # Now each of the interfaces should reply to a request to a proxied
579 self.pg0.add_stream(arp_req_pg0)
580 self.pg_enable_capture(self.pg_interfaces)
583 rx = self.pg0.get_capture(1)
584 self.verify_arp_resp(rx[0],
590 self.pg1.add_stream(arp_req_pg1)
591 self.pg_enable_capture(self.pg_interfaces)
594 rx = self.pg1.get_capture(1)
595 self.verify_arp_resp(rx[0],
601 self.pg2.add_stream(arp_req_pg2)
602 self.pg_enable_capture(self.pg_interfaces)
605 rx = self.pg2.get_capture(1)
606 self.verify_arp_resp(rx[0],
613 # A request for an address out of the configured range
615 arp_req_pg1_hi = (Ether(src=self.pg1.remote_mac,
616 dst="ff:ff:ff:ff:ff:ff") /
618 hwsrc=self.pg1.remote_mac,
620 psrc=self.pg1.remote_ip4))
621 self.send_and_assert_no_replies(self.pg1, arp_req_pg1_hi,
622 "ARP req out of range HI")
623 arp_req_pg1_low = (Ether(src=self.pg1.remote_mac,
624 dst="ff:ff:ff:ff:ff:ff") /
626 hwsrc=self.pg1.remote_mac,
628 psrc=self.pg1.remote_ip4))
629 self.send_and_assert_no_replies(self.pg1, arp_req_pg1_low,
630 "ARP req out of range Low")
633 # Request for an address in the proxy range but from an interface
636 self.send_and_assert_no_replies(self.pg3, arp_req_pg3,
637 "ARP req from different VRF")
640 # Disable Each interface for proxy ARP
641 # - expect none to respond
643 for i in self.pg_interfaces:
646 self.send_and_assert_no_replies(self.pg0, arp_req_pg0,
647 "ARP req from disable")
648 self.send_and_assert_no_replies(self.pg1, arp_req_pg1,
649 "ARP req from disable")
650 self.send_and_assert_no_replies(self.pg2, arp_req_pg2,
651 "ARP req from disable")
654 # clean up on interface 2
656 self.pg2.unset_unnumbered(self.pg1.sw_if_index)
662 # Interface 2 does not yet have ip4 config
664 self.pg2.config_ip4()
665 self.pg2.generate_remote_hosts(2)
668 # Add a reoute with out going label via an ARP unresolved next-hop
670 ip_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
671 [VppRoutePath(self.pg2.remote_hosts[1].ip4,
672 self.pg2.sw_if_index,
674 ip_10_0_0_1.add_vpp_config()
677 # packets should generate an ARP request
679 p = (Ether(src=self.pg0.remote_mac,
680 dst=self.pg0.local_mac) /
681 IP(src=self.pg0.remote_ip4, dst="10.0.0.1") /
682 UDP(sport=1234, dport=1234) /
685 self.pg0.add_stream(p)
686 self.pg_enable_capture(self.pg_interfaces)
689 rx = self.pg2.get_capture(1)
690 self.verify_arp_req(rx[0],
693 self.pg2._remote_hosts[1].ip4)
696 # now resolve the neighbours
698 self.pg2.configure_ipv4_neighbors()
701 # Now packet should be properly MPLS encapped.
702 # This verifies that MPLS link-type adjacencies are completed
703 # when the ARP entry resolves
705 self.pg0.add_stream(p)
706 self.pg_enable_capture(self.pg_interfaces)
709 rx = self.pg2.get_capture(1)
710 self.verify_ip_o_mpls(rx[0],
712 self.pg2.remote_hosts[1].mac,
716 self.pg2.unconfig_ip4()
718 if __name__ == '__main__':
719 unittest.main(testRunner=VppTestRunner)