Gratuitous ARP packet handling
only learn from a GARP packet if it is an update to an existing entry. Change-Id: I4c1b59cfedb911466e5e4c9756cf53a6676e1909 Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
This commit is contained in:
parent
180f6476f8
commit
59ae61ee75
@ -864,6 +864,7 @@ typedef enum
|
||||
_ (l3_type_not_ip4, "L3 type not IP4") \
|
||||
_ (l3_src_address_not_local, "IP4 source address not local to subnet") \
|
||||
_ (l3_dst_address_not_local, "IP4 destination address not local to subnet") \
|
||||
_ (l3_dst_address_unset, "IP4 destination address is unset") \
|
||||
_ (l3_src_address_is_local, "IP4 source address matches local interface") \
|
||||
_ (l3_src_address_learned, "ARP request IP4 source address learned") \
|
||||
_ (replies_received, "ARP replies received") \
|
||||
@ -980,6 +981,9 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
|
||||
(arp0->l3_type !=
|
||||
clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ?
|
||||
ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0);
|
||||
error0 =
|
||||
(0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ?
|
||||
ETHERNET_ARP_ERROR_l3_dst_address_unset : error0);
|
||||
|
||||
sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
|
||||
|
||||
@ -1110,7 +1114,23 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
|
||||
}
|
||||
}
|
||||
|
||||
if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags))
|
||||
if (fib_entry_is_sourced (dst_fei, FIB_SOURCE_ADJ))
|
||||
{
|
||||
/*
|
||||
* We matched an adj-fib on ths source subnet (a /32 previously
|
||||
* added as a result of ARP). If this request is a gratuitous
|
||||
* ARP, then learn from it.
|
||||
* The check for matching an adj-fib, is to prevent hosts
|
||||
* from spamming us with gratuitous ARPS that might otherwise
|
||||
* blow our ARP cache
|
||||
*/
|
||||
if (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
|
||||
arp0->ip4_over_ethernet[1].ip4.as_u32)
|
||||
error0 = arp_learn (vnm, am, sw_if_index0,
|
||||
&arp0->ip4_over_ethernet[0]);
|
||||
goto drop2;
|
||||
}
|
||||
else if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags))
|
||||
{
|
||||
error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
|
||||
goto drop1;
|
||||
@ -1152,11 +1172,17 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
|
||||
/* Learn or update sender's mapping only for replies to addresses
|
||||
* that are local to the subnet */
|
||||
if (arp0->opcode ==
|
||||
clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply) &&
|
||||
dst_is_local0)
|
||||
clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
|
||||
{
|
||||
error0 = arp_learn (vnm, am, sw_if_index0,
|
||||
&arp0->ip4_over_ethernet[0]);
|
||||
if (dst_is_local0)
|
||||
error0 = arp_learn (vnm, am, sw_if_index0,
|
||||
&arp0->ip4_over_ethernet[0]);
|
||||
else
|
||||
/* a reply for a non-local destination could be a GARP.
|
||||
* GARPs for hosts we know were handled above, so this one
|
||||
* we drop */
|
||||
error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
|
||||
|
||||
goto drop1;
|
||||
}
|
||||
else if (arp0->opcode ==
|
||||
@ -1227,9 +1253,8 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
|
||||
continue;
|
||||
|
||||
drop1:
|
||||
if (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ||
|
||||
(arp0->ip4_over_ethernet[0].ip4.as_u32 ==
|
||||
arp0->ip4_over_ethernet[1].ip4.as_u32))
|
||||
if (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
|
||||
arp0->ip4_over_ethernet[1].ip4.as_u32)
|
||||
{
|
||||
error0 = ETHERNET_ARP_ERROR_gratuitous_arp;
|
||||
goto drop2;
|
||||
|
@ -1214,6 +1214,104 @@ class ARPTestCase(VppTestCase):
|
||||
self.vapi.sw_interface_set_mac_address(self.pg2.sw_if_index,
|
||||
mac_string)
|
||||
|
||||
def test_garp(self):
|
||||
""" GARP """
|
||||
|
||||
#
|
||||
# Generate some hosts on the LAN
|
||||
#
|
||||
self.pg1.generate_remote_hosts(4)
|
||||
|
||||
#
|
||||
# And an ARP entry
|
||||
#
|
||||
arp = VppNeighbor(self,
|
||||
self.pg1.sw_if_index,
|
||||
self.pg1.remote_hosts[1].mac,
|
||||
self.pg1.remote_hosts[1].ip4)
|
||||
arp.add_vpp_config()
|
||||
|
||||
self.assertTrue(find_nbr(self,
|
||||
self.pg1.sw_if_index,
|
||||
self.pg1.remote_hosts[1].ip4,
|
||||
mac=self.pg1.remote_hosts[1].mac))
|
||||
|
||||
#
|
||||
# Send a GARP (request) to swap the host 1's address to that of host 2
|
||||
#
|
||||
p1 = (Ether(dst="ff:ff:ff:ff:ff:ff",
|
||||
src=self.pg1.remote_hosts[2].mac) /
|
||||
ARP(op="who-has",
|
||||
hwdst=self.pg1.local_mac,
|
||||
hwsrc=self.pg1.remote_hosts[2].mac,
|
||||
pdst=self.pg1.remote_hosts[1].ip4,
|
||||
psrc=self.pg1.remote_hosts[1].ip4))
|
||||
|
||||
self.pg1.add_stream(p1)
|
||||
self.pg_enable_capture(self.pg_interfaces)
|
||||
self.pg_start()
|
||||
|
||||
self.assertTrue(find_nbr(self,
|
||||
self.pg1.sw_if_index,
|
||||
self.pg1.remote_hosts[1].ip4,
|
||||
mac=self.pg1.remote_hosts[2].mac))
|
||||
|
||||
#
|
||||
# Send a GARP (reply) to swap the host 1's address to that of host 3
|
||||
#
|
||||
p1 = (Ether(dst="ff:ff:ff:ff:ff:ff",
|
||||
src=self.pg1.remote_hosts[3].mac) /
|
||||
ARP(op="is-at",
|
||||
hwdst=self.pg1.local_mac,
|
||||
hwsrc=self.pg1.remote_hosts[3].mac,
|
||||
pdst=self.pg1.remote_hosts[1].ip4,
|
||||
psrc=self.pg1.remote_hosts[1].ip4))
|
||||
|
||||
self.pg1.add_stream(p1)
|
||||
self.pg_enable_capture(self.pg_interfaces)
|
||||
self.pg_start()
|
||||
|
||||
self.assertTrue(find_nbr(self,
|
||||
self.pg1.sw_if_index,
|
||||
self.pg1.remote_hosts[1].ip4,
|
||||
mac=self.pg1.remote_hosts[3].mac))
|
||||
|
||||
#
|
||||
# GARPs (requets nor replies) for host we don't know yet
|
||||
# don't result in new neighbour entries
|
||||
#
|
||||
p1 = (Ether(dst="ff:ff:ff:ff:ff:ff",
|
||||
src=self.pg1.remote_hosts[3].mac) /
|
||||
ARP(op="who-has",
|
||||
hwdst=self.pg1.local_mac,
|
||||
hwsrc=self.pg1.remote_hosts[3].mac,
|
||||
pdst=self.pg1.remote_hosts[2].ip4,
|
||||
psrc=self.pg1.remote_hosts[2].ip4))
|
||||
|
||||
self.pg1.add_stream(p1)
|
||||
self.pg_enable_capture(self.pg_interfaces)
|
||||
self.pg_start()
|
||||
|
||||
self.assertFalse(find_nbr(self,
|
||||
self.pg1.sw_if_index,
|
||||
self.pg1.remote_hosts[2].ip4))
|
||||
|
||||
p1 = (Ether(dst="ff:ff:ff:ff:ff:ff",
|
||||
src=self.pg1.remote_hosts[3].mac) /
|
||||
ARP(op="is-at",
|
||||
hwdst=self.pg1.local_mac,
|
||||
hwsrc=self.pg1.remote_hosts[3].mac,
|
||||
pdst=self.pg1.remote_hosts[2].ip4,
|
||||
psrc=self.pg1.remote_hosts[2].ip4))
|
||||
|
||||
self.pg1.add_stream(p1)
|
||||
self.pg_enable_capture(self.pg_interfaces)
|
||||
self.pg_start()
|
||||
|
||||
self.assertFalse(find_nbr(self,
|
||||
self.pg1.sw_if_index,
|
||||
self.pg1.remote_hosts[2].ip4))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(testRunner=VppTestRunner)
|
||||
|
@ -9,7 +9,7 @@ from vpp_object import *
|
||||
from util import mactobinary
|
||||
|
||||
|
||||
def find_nbr(test, sw_if_index, ip_addr, is_static=0, inet=AF_INET):
|
||||
def find_nbr(test, sw_if_index, ip_addr, is_static=0, inet=AF_INET, mac=None):
|
||||
nbrs = test.vapi.ip_neighbor_dump(sw_if_index,
|
||||
is_ipv6=1 if AF_INET6 == inet else 0)
|
||||
if inet == AF_INET:
|
||||
@ -21,7 +21,11 @@ def find_nbr(test, sw_if_index, ip_addr, is_static=0, inet=AF_INET):
|
||||
for n in nbrs:
|
||||
if nbr_addr == n.ip_address[:s] \
|
||||
and is_static == n.is_static:
|
||||
return True
|
||||
if mac:
|
||||
if n.mac_address == mactobinary(mac):
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user