+ r1 = VppIpRoute(self, self.pg1.remote_hosts[10].ip4, 30,
+ [VppRoutePath(self.pg1.remote_hosts[10].ip4,
+ self.pg1.sw_if_index)])
+ r1.add_vpp_config()
+
+ self.pg1.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ rx = self.pg1.get_capture(1)
+ self.verify_arp_resp(rx[0],
+ self.pg1.local_mac,
+ self.pg1.remote_mac,
+ self.pg1.local_ip4,
+ self.pg1.remote_hosts[10].ip4)
+
+ r2 = VppIpRoute(self, self.pg1.remote_hosts[10].ip4, 32,
+ [VppRoutePath(self.pg1.remote_hosts[10].ip4,
+ self.pg1.sw_if_index)])
+ r2.add_vpp_config()
+
+ self.pg1.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ rx = self.pg1.get_capture(1)
+ self.verify_arp_resp(rx[0],
+ self.pg1.local_mac,
+ self.pg1.remote_mac,
+ self.pg1.local_ip4,
+ self.pg1.remote_hosts[10].ip4)
+
+ #
+ # add an ARP entry that's not on the sub-net and so whose
+ # adj-fib fails the refinement check. then send an ARP request
+ # from that source
+ #
+ a1 = VppNeighbor(self,
+ self.pg0.sw_if_index,
+ self.pg0.remote_mac,
+ "100.100.100.50")
+ a1.add_vpp_config()
+
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg0.remote_mac,
+ psrc="100.100.100.50",
+ pdst=self.pg0.remote_ip4))
+ self.send_and_assert_no_replies(self.pg0, p,
+ "ARP req for from failed adj-fib")
+
+ #
+ # ERROR Cases
+ # 1 - don't respond to ARP request for address not within the
+ # interface's sub-net
+ # 1b - nor within the unnumbered subnet
+ # 1c - nor within the subnet of a different interface
+ #
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg0.remote_mac,
+ pdst="10.10.10.3",
+ psrc=self.pg0.remote_ip4))
+ self.send_and_assert_no_replies(self.pg0, p,
+ "ARP req for non-local destination")
+ self.assertFalse(find_nbr(self,
+ self.pg0.sw_if_index,
+ "10.10.10.3"))
+
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg2.remote_mac,
+ pdst="10.10.10.3",
+ psrc=self.pg1.remote_hosts[7].ip4))
+ self.send_and_assert_no_replies(
+ self.pg0, p,
+ "ARP req for non-local destination - unnum")
+
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg0.remote_mac,
+ pdst=self.pg1.local_ip4,
+ psrc=self.pg1.remote_ip4))
+ self.send_and_assert_no_replies(self.pg0, p,
+ "ARP req diff sub-net")
+ self.assertFalse(find_nbr(self,
+ self.pg0.sw_if_index,
+ self.pg1.remote_ip4))
+
+ #
+ # 2 - don't respond to ARP request from an address not within the
+ # interface's sub-net
+ # 2b - to a proxied address
+ # 2c - not within a different interface's sub-net
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg0.remote_mac,
+ psrc="10.10.10.3",
+ pdst=self.pg0.local_ip4))
+ self.send_and_assert_no_replies(self.pg0, p,
+ "ARP req for non-local source")
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg2.remote_mac,
+ psrc="10.10.10.3",
+ pdst=self.pg0.local_ip4))
+ self.send_and_assert_no_replies(
+ self.pg0, p,
+ "ARP req for non-local source - unnum")
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg0.remote_mac,
+ psrc=self.pg1.remote_ip4,
+ pdst=self.pg0.local_ip4))
+ self.send_and_assert_no_replies(self.pg0, p,
+ "ARP req for non-local source 2c")
+
+ #
+ # 3 - don't respond to ARP request from an address that belongs to
+ # the router
+ #
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg0.remote_mac,
+ psrc=self.pg0.local_ip4,
+ pdst=self.pg0.local_ip4))
+ self.send_and_assert_no_replies(self.pg0, p,
+ "ARP req for non-local source")
+
+ #
+ # 4 - don't respond to ARP requests that has mac source different
+ # from ARP request HW source
+ #
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
+ ARP(op="who-has",
+ hwsrc="00:00:00:DE:AD:BE",
+ psrc=self.pg0.remote_ip4,
+ pdst=self.pg0.local_ip4))
+ self.send_and_assert_no_replies(self.pg0, p,
+ "ARP req for non-local source")
+
+ #
+ # 5 - don't respond to ARP requests for address within the
+ # interface's sub-net but not the interface's address
+ #
+ self.pg0.generate_remote_hosts(2)
+ p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
+ ARP(op="who-has",
+ hwsrc=self.pg0.remote_mac,
+ psrc=self.pg0.remote_hosts[0].ip4,
+ pdst=self.pg0.remote_hosts[1].ip4))
+ self.send_and_assert_no_replies(self.pg0, p,
+ "ARP req for non-local destination")
+
+ #
+ # cleanup
+ #
+ static_arp.remove_vpp_config()
+ self.pg2.unset_unnumbered(self.pg1.sw_if_index)
+
+ # need this to flush the adj-fibs
+ self.pg2.unset_unnumbered(self.pg1.sw_if_index)
+ self.pg2.admin_down()
+ self.pg1.admin_down()
+
+ def test_proxy_mirror_arp(self):
+ """ Interface Mirror Proxy ARP """
+
+ #
+ # When VPP has an interface whose address is also applied to a TAP
+ # interface on the host, then VPP's TAP interface will be unnumbered
+ # to the 'real' interface and do proxy ARP from the host.
+ # the curious aspect of this setup is that ARP requests from the host
+ # will come from the VPP's own address.
+ #
+ self.pg0.generate_remote_hosts(2)
+
+ arp_req_from_me = (Ether(src=self.pg2.remote_mac,
+ dst="ff:ff:ff:ff:ff:ff") /
+ ARP(op="who-has",
+ hwsrc=self.pg2.remote_mac,
+ pdst=self.pg0.remote_hosts[1].ip4,
+ psrc=self.pg0.local_ip4))
+
+ #
+ # Configure Proxy ARP for the subnet on PG0addresses on pg0
+ #
+ self.vapi.proxy_arp_add_del(proxy={'table_id': 0,
+ 'low': self.pg0._local_ip4_subnet,
+ 'hi': self.pg0._local_ip4_bcast},
+ is_add=1)
+
+ # Make pg2 un-numbered to pg0
+ #
+ self.pg2.set_unnumbered(self.pg0.sw_if_index)
+
+ #
+ # Enable pg2 for proxy ARP
+ #
+ self.pg2.set_proxy_arp()
+
+ #
+ # Send the ARP request with an originating address that
+ # is VPP's own address
+ #
+ rx = self.send_and_expect(self.pg2, [arp_req_from_me], self.pg2)
+ self.verify_arp_resp(rx[0],
+ self.pg2.local_mac,
+ self.pg2.remote_mac,
+ self.pg0.remote_hosts[1].ip4,
+ self.pg0.local_ip4)
+
+ #
+ # validate we have not learned an ARP entry as a result of this
+ #
+ self.assertFalse(find_nbr(self,
+ self.pg2.sw_if_index,
+ self.pg0.local_ip4))
+
+ #
+ # setup a punt redirect so packets from the uplink go to the tap
+ #
+ redirect = VppIpPuntRedirect(self, self.pg0.sw_if_index,
+ self.pg2.sw_if_index, self.pg0.local_ip4)
+ redirect.add_vpp_config()
+
+ p_tcp = (Ether(src=self.pg0.remote_mac,
+ dst=self.pg0.local_mac,) /
+ IP(src=self.pg0.remote_ip4,
+ dst=self.pg0.local_ip4) /
+ TCP(sport=80, dport=80) /
+ Raw())
+ rx = self.send_and_expect(self.pg0, [p_tcp], self.pg2)
+
+ # there's no ARP entry so this is an ARP req
+ self.assertTrue(rx[0].haslayer(ARP))
+
+ # and ARP entry for VPP's pg0 address on the host interface
+ n1 = VppNeighbor(self,
+ self.pg2.sw_if_index,
+ self.pg2.remote_mac,
+ self.pg0.local_ip4,
+ is_no_fib_entry=True).add_vpp_config()
+ # now the packets shold forward
+ rx = self.send_and_expect(self.pg0, [p_tcp], self.pg2)
+ self.assertFalse(rx[0].haslayer(ARP))
+ self.assertEqual(rx[0][Ether].dst, self.pg2.remote_mac)
+
+ #
+ # flush the neighbor cache on the uplink
+ #
+ af = VppEnum.vl_api_address_family_t
+ self.vapi.ip_neighbor_flush(af.ADDRESS_IP4, self.pg0.sw_if_index)
+
+ # ensure we can still resolve the ARPs on the uplink
+ self.pg0.resolve_arp()
+
+ self.assertTrue(find_nbr(self,
+ self.pg0.sw_if_index,
+ self.pg0.remote_ip4))
+
+ #
+ # cleanup
+ #
+ self.vapi.proxy_arp_add_del(proxy={'table_id': 0,
+ 'low': self.pg0._local_ip4_subnet,
+ 'hi': self.pg0._local_ip4_bcast},
+ is_add=0)
+ redirect.remove_vpp_config()
+
+ def test_proxy_arp(self):
+ """ Proxy ARP """
+
+ self.pg1.generate_remote_hosts(2)
+
+ #
+ # Proxy ARP request packets for each interface
+ #
+ arp_req_pg0 = (Ether(src=self.pg0.remote_mac,
+ dst="ff:ff:ff:ff:ff:ff") /
+ ARP(op="who-has",
+ hwsrc=self.pg0.remote_mac,
+ pdst="10.10.10.3",
+ psrc=self.pg0.remote_ip4))
+ arp_req_pg0_tagged = (Ether(src=self.pg0.remote_mac,
+ dst="ff:ff:ff:ff:ff:ff") /
+ Dot1Q(vlan=0) /
+ ARP(op="who-has",
+ hwsrc=self.pg0.remote_mac,
+ pdst="10.10.10.3",
+ psrc=self.pg0.remote_ip4))
+ arp_req_pg1 = (Ether(src=self.pg1.remote_mac,
+ dst="ff:ff:ff:ff:ff:ff") /
+ ARP(op="who-has",
+ hwsrc=self.pg1.remote_mac,
+ pdst="10.10.10.3",
+ psrc=self.pg1.remote_ip4))
+ arp_req_pg2 = (Ether(src=self.pg2.remote_mac,
+ dst="ff:ff:ff:ff:ff:ff") /
+ ARP(op="who-has",
+ hwsrc=self.pg2.remote_mac,
+ pdst="10.10.10.3",
+ psrc=self.pg1.remote_hosts[1].ip4))
+ arp_req_pg3 = (Ether(src=self.pg3.remote_mac,
+ dst="ff:ff:ff:ff:ff:ff") /
+ ARP(op="who-has",
+ hwsrc=self.pg3.remote_mac,
+ pdst="10.10.10.3",
+ psrc=self.pg3.remote_ip4))
+
+ #
+ # Configure Proxy ARP for 10.10.10.0 -> 10.10.10.124
+ #
+ self.vapi.proxy_arp_add_del(proxy={'table_id': 0,
+ 'low': "10.10.10.2",
+ 'hi': "10.10.10.124"},
+ is_add=1)
+
+ #
+ # No responses are sent when the interfaces are not enabled for proxy
+ # ARP
+ #
+ self.send_and_assert_no_replies(self.pg0, arp_req_pg0,
+ "ARP req from unconfigured interface")
+ self.send_and_assert_no_replies(self.pg2, arp_req_pg2,
+ "ARP req from unconfigured interface")
+
+ #
+ # Make pg2 un-numbered to pg1
+ # still won't reply.
+ #
+ self.pg2.set_unnumbered(self.pg1.sw_if_index)
+
+ self.send_and_assert_no_replies(self.pg2, arp_req_pg2,
+ "ARP req from unnumbered interface")
+
+ #
+ # Enable each interface to reply to proxy ARPs
+ #
+ for i in self.pg_interfaces:
+ i.set_proxy_arp()
+
+ #
+ # Now each of the interfaces should reply to a request to a proxied
+ # address
+ #
+ self.pg0.add_stream(arp_req_pg0)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg0.get_capture(1)
+ self.verify_arp_resp(rx[0],
+ self.pg0.local_mac,
+ self.pg0.remote_mac,
+ "10.10.10.3",
+ self.pg0.remote_ip4)
+
+ self.pg0.add_stream(arp_req_pg0_tagged)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg0.get_capture(1)
+ self.verify_arp_resp(rx[0],
+ self.pg0.local_mac,
+ self.pg0.remote_mac,
+ "10.10.10.3",
+ self.pg0.remote_ip4)
+
+ self.pg1.add_stream(arp_req_pg1)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg1.get_capture(1)
+ self.verify_arp_resp(rx[0],
+ self.pg1.local_mac,
+ self.pg1.remote_mac,
+ "10.10.10.3",
+ self.pg1.remote_ip4)
+
+ self.pg2.add_stream(arp_req_pg2)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg2.get_capture(1)
+ self.verify_arp_resp(rx[0],
+ self.pg2.local_mac,
+ self.pg2.remote_mac,
+ "10.10.10.3",
+ self.pg1.remote_hosts[1].ip4)
+
+ #
+ # A request for an address out of the configured range
+ #
+ arp_req_pg1_hi = (Ether(src=self.pg1.remote_mac,
+ dst="ff:ff:ff:ff:ff:ff") /
+ ARP(op="who-has",
+ hwsrc=self.pg1.remote_mac,
+ pdst="10.10.10.125",
+ psrc=self.pg1.remote_ip4))
+ self.send_and_assert_no_replies(self.pg1, arp_req_pg1_hi,
+ "ARP req out of range HI")
+ arp_req_pg1_low = (Ether(src=self.pg1.remote_mac,
+ dst="ff:ff:ff:ff:ff:ff") /
+ ARP(op="who-has",
+ hwsrc=self.pg1.remote_mac,
+ pdst="10.10.10.1",
+ psrc=self.pg1.remote_ip4))
+ self.send_and_assert_no_replies(self.pg1, arp_req_pg1_low,
+ "ARP req out of range Low")
+
+ #
+ # Request for an address in the proxy range but from an interface
+ # in a different VRF
+ #
+ self.send_and_assert_no_replies(self.pg3, arp_req_pg3,
+ "ARP req from different VRF")
+
+ #
+ # Disable Each interface for proxy ARP
+ # - expect none to respond
+ #
+ for i in self.pg_interfaces:
+ i.set_proxy_arp(0)
+
+ self.send_and_assert_no_replies(self.pg0, arp_req_pg0,
+ "ARP req from disable")
+ self.send_and_assert_no_replies(self.pg1, arp_req_pg1,