ARP/ND use path_remove to complement path_add
[vpp.git] / src / vnet / ethernet / arp.c
index bfcd357..624c48c 100644 (file)
@@ -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);
@@ -580,11 +582,11 @@ vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm,
          fib_index =
            ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index);
          e->fib_entry_index =
-           fib_table_entry_update_one_path (fib_index, &pfx, FIB_SOURCE_ADJ,
-                                            FIB_ENTRY_FLAG_ATTACHED,
-                                            FIB_PROTOCOL_IP4, &pfx.fp_addr,
-                                            e->sw_if_index, ~0, 1, NULL,
-                                            FIB_ROUTE_PATH_FLAG_NONE);
+           fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ,
+                                     FIB_ENTRY_FLAG_ATTACHED,
+                                     FIB_PROTOCOL_IP4, &pfx.fp_addr,
+                                     e->sw_if_index, ~0, 1, NULL,
+                                     FIB_ROUTE_PATH_FLAG_NONE);
        }
       else
        {
@@ -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;
@@ -1662,7 +1673,23 @@ arp_entry_free (ethernet_arp_interface_t * eai, ethernet_arp_ip4_entry_t * e)
   ethernet_arp_main_t *am = &ethernet_arp_main;
 
   if (FIB_NODE_INDEX_INVALID != e->fib_entry_index)
-    fib_table_entry_delete_index (e->fib_entry_index, FIB_SOURCE_ADJ);
+    {
+      fib_prefix_t pfx = {
+       .fp_len = 32,
+       .fp_proto = FIB_PROTOCOL_IP4,
+       .fp_addr.ip4 = e->ip4_address,
+      };
+      u32 fib_index;
+
+      fib_index = ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index);
+
+      fib_table_entry_path_remove (fib_index, &pfx,
+                                  FIB_SOURCE_ADJ,
+                                  FIB_PROTOCOL_IP4,
+                                  &pfx.fp_addr,
+                                  e->sw_if_index, ~0, 1,
+                                  FIB_ROUTE_PATH_FLAG_NONE);
+    }
   hash_unset (eai->arp_entries, e->ip4_address.as_u32);
   pool_put (am->ip4_entry_pool, e);
 }
@@ -2170,6 +2197,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 +2246,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;