_ (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") \
(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];
}
}
- 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;
/* 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 ==
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;
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)