From cb9ab47fd388c237fe0bad53d07e99096d338ac8 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 16 May 2017 21:35:56 -0500 Subject: [PATCH] VPP-719: Accept ARP replies from VRRP hw addr Check whether an ARP src hw addr starts with 00:00:5e:00:01 before rejecting due to a mismatch between ARP src hw addr and ethernet frame src addr. Change-Id: Ia3ecd5d6dba34876aca8d90bc622a0a1397e48fb Signed-off-by: Matthew Smith --- src/vnet/ethernet/arp.c | 34 +++++++++++++++++----- test/test_neighbor.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 7 deletions(-) diff --git a/src/vnet/ethernet/arp.c b/src/vnet/ethernet/arp.c index bfcd3573885..f44cb594dea 100644 --- a/src/vnet/ethernet/arp.c +++ b/src/vnet/ethernet/arp.c @@ -107,6 +107,8 @@ typedef struct #define ETHERNET_ARP_ARGS_POPULATE (1<<2) } vnet_arp_set_ip4_over_ethernet_rpc_args_t; +static const u8 vrrp_prefix[] = { 0x00, 0x00, 0x5E, 0x00, 0x01 }; + static void set_ip4_over_ethernet_rpc_callback (vnet_arp_set_ip4_over_ethernet_rpc_args_t * a); @@ -991,7 +993,7 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) ethernet_header_t *eth0; ip4_address_t *if_addr0, proxy_src; u32 pi0, error0, next0, sw_if_index0, conn_sw_if_index0, fib_index0; - u8 is_request0, dst_is_local0, is_unnum0; + u8 is_request0, dst_is_local0, is_unnum0, is_vrrp_reply0; ethernet_proxy_arp_t *pa; fib_node_index_t dst_fei, src_fei; fib_prefix_t pfx0; @@ -1097,10 +1099,19 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) /* Fill in ethernet header. */ eth0 = ethernet_buffer_get_header (p0); + is_vrrp_reply0 = + ((arp0->opcode == + clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)) + && + (!memcmp + (arp0->ip4_over_ethernet[0].ethernet, vrrp_prefix, + sizeof (vrrp_prefix)))); + /* Trash ARP packets whose ARP-level source addresses do not - match their L2-frame-level source addresses */ + match their L2-frame-level source addresses, unless it's + a reply from a VRRP virtual router */ if (memcmp (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet, - sizeof (eth0->src_address))) + sizeof (eth0->src_address)) && !is_vrrp_reply0) { error0 = ETHERNET_ARP_ERROR_l2_address_mismatch; goto drop2; @@ -2170,6 +2181,7 @@ arp_term_l2bd (vlib_main_t * vm, u16 bd_index0; u32 ip0; u8 *macp0; + u8 is_vrrp_reply0; pi0 = from[0]; to_next[0] = pi0; @@ -2218,12 +2230,20 @@ arp_term_l2bd (vlib_main_t * vm, if (error0) goto drop; + is_vrrp_reply0 = + ((arp0->opcode == + clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)) + && + (!memcmp + (arp0->ip4_over_ethernet[0].ethernet, vrrp_prefix, + sizeof (vrrp_prefix)))); + /* Trash ARP packets whose ARP-level source addresses do not - match their L2-frame-level source addresses */ + match their L2-frame-level source addresses, unless it's + a reply from a VRRP virtual router */ if (PREDICT_FALSE - (memcmp - (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet, - sizeof (eth0->src_address)))) + (memcmp (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet, + sizeof (eth0->src_address)) && !is_vrrp_reply0)) { error0 = ETHERNET_ARP_ERROR_l2_address_mismatch; goto drop; diff --git a/test/test_neighbor.py b/test/test_neighbor.py index 48c6a291248..67d64a20faa 100644 --- a/test/test_neighbor.py +++ b/test/test_neighbor.py @@ -87,6 +87,24 @@ class ARPTestCase(VppTestCase): self.assertEqual(arp.psrc, sip) self.assertEqual(arp.pdst, dip) + def verify_arp_vrrp_resp(self, rx, smac, dmac, sip, dip): + ether = rx[Ether] + self.assertEqual(ether.dst, dmac) + self.assertEqual(ether.src, smac) + + arp = rx[ARP] + self.assertEqual(arp.hwtype, 1) + self.assertEqual(arp.ptype, 0x800) + self.assertEqual(arp.hwlen, 6) + self.assertEqual(arp.plen, 4) + self.assertEqual(arp.op, arp_opts["is-at"]) + self.assertNotEqual(arp.hwsrc, smac) + self.assertTrue("00:00:5e:00:01" in arp.hwsrc or + "00:00:5E:00:01" in arp.hwsrc) + self.assertEqual(arp.hwdst, dmac) + self.assertEqual(arp.psrc, sip) + self.assertEqual(arp.pdst, dip) + def verify_ip(self, rx, smac, dmac, sip, dip): ether = rx[Ether] self.assertEqual(ether.dst, dmac) @@ -737,5 +755,62 @@ class ARPTestCase(VppTestCase): "10.0.0.1") self.pg2.unconfig_ip4() + def test_arp_vrrp(self): + """ ARP reply with VRRP virtual src hw addr """ + + # + # IP packet destined for pg1 remote host arrives on pg0 resulting + # in an ARP request for the address of the remote host on pg1 + # + p0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + UDP(sport=1234, dport=1234) / + Raw()) + + self.pg0.add_stream(p0) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx1 = self.pg1.get_capture(1) + + self.verify_arp_req(rx1[0], + self.pg1.local_mac, + self.pg1.local_ip4, + self.pg1.remote_ip4) + + # + # ARP reply for address of pg1 remote host arrives on pg1 with + # the hw src addr set to a value in the VRRP IPv4 range of + # MAC addresses + # + p1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + ARP(op="is-at", hwdst=self.pg1.local_mac, + hwsrc="00:00:5e:00:01:09", pdst=self.pg1.local_ip4, + psrc=self.pg1.remote_ip4)) + + self.pg1.add_stream(p1) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # + # IP packet destined for pg1 remote host arrives on pg0 again. + # VPP should have an ARP entry for that address now and the packet + # should be sent out pg1. + # + self.pg0.add_stream(p0) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx1 = self.pg1.get_capture(1) + + self.verify_ip(rx1[0], + self.pg1.local_mac, + "00:00:5e:00:01:09", + self.pg0.remote_ip4, + self.pg1.remote_ip4) + + self.pg1.admin_down() + self.pg1.admin_up() + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) -- 2.16.6