arp: check for manually added proxy-arp entries
[vpp.git] / src / vnet / arp / arp.c
index 14a1ae9..d39d48e 100644 (file)
 #include <vnet/fib/fib_entry_src.h>
 #include <vnet/adj/adj_nbr.h>
 #include <vnet/adj/adj_mcast.h>
+#include <vnet/pg/pg.h>
 
 #include <vnet/ip-neighbor/ip_neighbor.h>
+#include <vnet/ip-neighbor/ip4_neighbor.h>
 #include <vnet/ip-neighbor/ip_neighbor_dp.h>
 
 #include <vlibmemory/api.h>
@@ -189,16 +191,20 @@ always_inline u32
 arp_learn (u32 sw_if_index,
           const ethernet_arp_ip4_over_ethernet_address_t * addr)
 {
+  /* *INDENT-OFF* */
   ip_neighbor_learn_t l = {
-    .ip.ip4 = addr->ip4,
-    .type = IP46_TYPE_IP4,
+    .ip = {
+      .ip.ip4 = addr->ip4,
+      .version = AF_IP4,
+    },
     .mac = addr->mac,
     .sw_if_index = sw_if_index,
   };
+  /* *INDENT-ON* */
 
   ip_neighbor_learn_dp (&l);
 
-  return (ETHERNET_ARP_ERROR_l3_src_address_learned);
+  return (ARP_ERROR_L3_SRC_ADDRESS_LEARNED);
 }
 
 typedef enum arp_input_next_t_
@@ -243,22 +249,21 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
          p0 = vlib_get_buffer (vm, pi0);
          arp0 = vlib_buffer_get_current (p0);
 
-         error0 = ETHERNET_ARP_ERROR_replies_sent;
+         error0 = ARP_ERROR_REPLIES_SENT;
          next0 = ARP_INPUT_NEXT_DROP;
 
-         error0 =
-           (arp0->l2_type !=
-            clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet) ?
-            ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0);
-         error0 =
-           (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);
-
-         if (ETHERNET_ARP_ERROR_replies_sent == error0)
+         error0 = (arp0->l2_type != clib_net_to_host_u16 (
+                                      ETHERNET_ARP_HARDWARE_TYPE_ethernet) ?
+                           ARP_ERROR_L2_TYPE_NOT_ETHERNET :
+                           error0);
+         error0 = (arp0->l3_type != clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ?
+                           ARP_ERROR_L3_TYPE_NOT_IP4 :
+                           error0);
+         error0 = (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ?
+                           ARP_ERROR_L3_DST_ADDRESS_UNSET :
+                           error0);
+
+         if (ARP_ERROR_REPLIES_SENT == error0)
            {
              next0 = ARP_INPUT_NEXT_DISABLED;
              vnet_feature_arc_start (am->feature_arc_index,
@@ -284,23 +289,6 @@ typedef enum arp_disabled_next_t_
   ARP_DISABLED_N_NEXT,
 } arp_disabled_next_t;
 
-#define foreach_arp_disabled_error                                     \
-  _ (DISABLED, "ARP Disabled on this interface")                    \
-
-typedef enum
-{
-#define _(sym,string) ARP_DISABLED_ERROR_##sym,
-  foreach_arp_disabled_error
-#undef _
-    ARP_DISABLED_N_ERROR,
-} arp_disabled_error_t;
-
-static char *arp_disabled_error_strings[] = {
-#define _(sym,string) string,
-  foreach_arp_disabled_error
-#undef _
-};
-
 static uword
 arp_disabled (vlib_main_t * vm,
              vlib_node_runtime_t * node, vlib_frame_t * frame)
@@ -327,7 +315,7 @@ arp_disabled (vlib_main_t * vm,
          u32 pi0, error0;
 
          next0 = ARP_DISABLED_NEXT_DROP;
-         error0 = ARP_DISABLED_ERROR_DISABLED;
+         error0 = ARP_ERROR_DISABLED;
 
          pi0 = to_next[0] = from[0];
          from += 1;
@@ -427,14 +415,14 @@ arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
          eth_rx = ethernet_buffer_get_header (p0);
 
          next0 = ARP_REPLY_NEXT_DROP;
-         error0 = ETHERNET_ARP_ERROR_replies_sent;
+         error0 = ARP_ERROR_REPLIES_SENT;
          sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
 
          /* Check that IP address is local and matches incoming interface. */
          fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
          if (~0 == fib_index0)
            {
-             error0 = ETHERNET_ARP_ERROR_interface_no_table;
+             error0 = ARP_ERROR_INTERFACE_NO_TABLE;
              goto drop;
 
            }
@@ -480,34 +468,34 @@ arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
                      address. */
                   if (FIB_ENTRY_FLAG_LOCAL & src_flags)
                     {
-                      error0 = ETHERNET_ARP_ERROR_l3_src_address_is_local;
-                      /*
-                       * 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.
-                       * So don't drop immediately here, instead go see if this
-                       * is a proxy ARP case.
-                       */
-                      goto next_feature;
-                    }
-                  /* A Source must also be local to subnet of matching
-                   * interface address. */
-                  if ((FIB_ENTRY_FLAG_ATTACHED & src_flags) ||
-                      (FIB_ENTRY_FLAG_CONNECTED & src_flags))
-                    {
-                      attached = 1;
-                      break;
-                    }
-                  /*
-                   * else
-                   *  The packet was sent from an address that is not
-                   *  connected nor attached i.e. it is not from an
-                   *  address that is covered by a link's sub-net,
-                   *  nor is it a already learned host resp.
-                   */
+                     error0 = ARP_ERROR_L3_SRC_ADDRESS_IS_LOCAL;
+                     /*
+                      * 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.
+                      * So don't drop immediately here, instead go see if this
+                      * is a proxy ARP case.
+                      */
+                     goto next_feature;
+                   }
+                 /* A Source must also be local to subnet of matching
+                  * interface address. */
+                 if ((FIB_ENTRY_FLAG_ATTACHED & src_flags) ||
+                     (FIB_ENTRY_FLAG_CONNECTED & src_flags))
+                   {
+                     attached = 1;
+                     break;
+                   }
+                 /*
+                  * else
+                  *  The packet was sent from an address that is not
+                  *  connected nor attached i.e. it is not from an
+                  *  address that is covered by a link's sub-net,
+                  *  nor is it a already learned host resp.
+                  */
                 }));
                 /* *INDENT-ON* */
 
@@ -535,7 +523,7 @@ arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
                 * configuration. If the matching route is not a host route
                 * (i.e. a /32)
                 */
-               error0 = ETHERNET_ARP_ERROR_l3_src_address_not_local;
+               error0 = ARP_ERROR_L3_SRC_ADDRESS_NOT_LOCAL;
                goto drop;
              }
          }
@@ -543,6 +531,8 @@ arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
          dst_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
                                          &arp0->ip4_over_ethernet[1].ip4,
                                          32);
+         conn_sw_if_index0 = fib_entry_get_any_resolving_interface (dst_fei);
+
          switch (arp_dst_fib_check (dst_fei, &dst_flags))
            {
            case ARP_DST_FIB_ADJ:
@@ -554,17 +544,25 @@ arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
               * 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 (sw_if_index0, &arp0->ip4_over_ethernet[0]);
-             goto drop;
+             if (conn_sw_if_index0 != sw_if_index0)
+               error0 = ARP_ERROR_L3_DST_ADDRESS_NOT_LOCAL;
+             else if (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
+                      arp0->ip4_over_ethernet[1].ip4.as_u32)
+               {
+                 vlib_increment_simple_counter (
+                   &ip_neighbor_counters[AF_IP4]
+                      .ipnc[VLIB_RX][IP_NEIGHBOR_CTR_GRAT],
+                   vm->thread_index, sw_if_index0, 1);
+                 error0 =
+                   arp_learn (sw_if_index0, &arp0->ip4_over_ethernet[0]);
+               }
+             goto next_feature;
            case ARP_DST_FIB_CONN:
              /* destination is connected, continue to process */
              break;
            case ARP_DST_FIB_NONE:
              /* destination is not connected, stop here */
-             error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
+             error0 = ARP_ERROR_L3_DST_ADDRESS_NOT_LOCAL;
              goto next_feature;
            }
 
@@ -587,10 +585,18 @@ arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
              (eth_rx->src_address,
               arp0->ip4_over_ethernet[0].mac.bytes) && !is_vrrp_reply0)
            {
-             error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
+             error0 = ARP_ERROR_L2_ADDRESS_MISMATCH;
              goto drop;
            }
 
+         vlib_increment_simple_counter (
+           &ip_neighbor_counters[AF_IP4]
+              .ipnc[VLIB_RX][arp0->opcode == clib_host_to_net_u16 (
+                                               ETHERNET_ARP_OPCODE_reply) ?
+                                     IP_NEIGHBOR_CTR_REPLY :
+                                     IP_NEIGHBOR_CTR_REQUEST],
+           vm->thread_index, sw_if_index0, 1);
+
          /* Learn or update sender's mapping only for replies to addresses
           * that are local to the subnet */
          if (arp0->opcode ==
@@ -603,7 +609,7 @@ arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
                /* 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;
+               error0 = ARP_ERROR_L3_DST_ADDRESS_NOT_LOCAL;
 
              goto next_feature;
            }
@@ -615,7 +621,6 @@ arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
            }
 
          /* Honor unnumbered interface, if any */
-         conn_sw_if_index0 = fib_entry_get_resolving_interface (dst_fei);
          if (sw_if_index0 != conn_sw_if_index0 ||
              sw_if_index0 != fib_entry_get_resolving_interface (src_fei))
            {
@@ -626,14 +631,14 @@ arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
               */
              if (!arp_unnumbered (p0, sw_if_index0, conn_sw_if_index0))
                {
-                 error0 = ETHERNET_ARP_ERROR_unnumbered_mismatch;
+                 error0 = ARP_ERROR_UNNUMBERED_MISMATCH;
                  goto drop;
                }
            }
          if (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
              arp0->ip4_over_ethernet[1].ip4.as_u32)
            {
-             error0 = ETHERNET_ARP_ERROR_gratuitous_arp;
+             error0 = ARP_ERROR_GRATUITOUS_ARP;
              goto drop;
            }
 
@@ -645,12 +650,14 @@ arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
          if (!error0)
            error0 = arp_learn (sw_if_index0, &arp0->ip4_over_ethernet[1]);
 
+         vlib_increment_simple_counter (
+           &ip_neighbor_counters[AF_IP4].ipnc[VLIB_TX][IP_NEIGHBOR_CTR_REPLY],
+           vm->thread_index, sw_if_index0, 1);
          n_replies_sent += 1;
          goto enqueue;
 
        next_feature:
          vnet_feature_next (&next0, p0);
-         goto enqueue;
 
        drop:
          p0->error = node->errors[error0];
@@ -663,19 +670,13 @@ arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     }
 
-  vlib_error_count (vm, node->node_index,
-                   ETHERNET_ARP_ERROR_replies_sent, n_replies_sent);
+  vlib_error_count (vm, node->node_index, ARP_ERROR_REPLIES_SENT,
+                   n_replies_sent);
 
   return frame->n_vectors;
 }
 
 
-static char *ethernet_arp_error_strings[] = {
-#define _(sym,string) string,
-  foreach_ethernet_arp_error
-#undef _
-};
-
 /* *INDENT-OFF* */
 
 VLIB_REGISTER_NODE (arp_input_node, static) =
@@ -683,8 +684,8 @@ VLIB_REGISTER_NODE (arp_input_node, static) =
   .function = arp_input,
   .name = "arp-input",
   .vector_size = sizeof (u32),
-  .n_errors = ETHERNET_ARP_N_ERROR,
-  .error_strings = ethernet_arp_error_strings,
+  .n_errors = ARP_N_ERROR,
+  .error_counters = arp_error_counters,
   .n_next_nodes = ARP_INPUT_N_NEXT,
   .next_nodes = {
     [ARP_INPUT_NEXT_DROP] = "error-drop",
@@ -699,8 +700,8 @@ VLIB_REGISTER_NODE (arp_disabled_node, static) =
   .function = arp_disabled,
   .name = "arp-disabled",
   .vector_size = sizeof (u32),
-  .n_errors = ARP_DISABLED_N_ERROR,
-  .error_strings = arp_disabled_error_strings,
+  .n_errors = ARP_N_ERROR,
+  .error_counters = arp_error_counters,
   .n_next_nodes = ARP_DISABLED_N_NEXT,
   .next_nodes = {
     [ARP_INPUT_NEXT_DROP] = "error-drop",
@@ -714,8 +715,8 @@ VLIB_REGISTER_NODE (arp_reply_node, static) =
   .function = arp_reply,
   .name = "arp-reply",
   .vector_size = sizeof (u32),
-  .n_errors = ETHERNET_ARP_N_ERROR,
-  .error_strings = ethernet_arp_error_strings,
+  .n_errors = ARP_N_ERROR,
+  .error_counters = arp_error_counters,
   .n_next_nodes = ARP_REPLY_N_NEXT,
   .next_nodes = {
     [ARP_REPLY_NEXT_DROP] = "error-drop",
@@ -852,17 +853,8 @@ static clib_error_t *
 vnet_arp_add_del_sw_interface (vnet_main_t * vnm, u32 sw_if_index, u32 is_add)
 {
   ethernet_arp_main_t *am = &ethernet_arp_main;
-
-  if (!is_add && sw_if_index != ~0)
-    {
-      arp_disable (am, sw_if_index);
-    }
-  else if (is_add)
-    {
-      vnet_feature_enable_disable ("arp", "arp-disabled",
-                                  sw_if_index, 1, NULL, 0);
-    }
-
+  if (is_add)
+    arp_disable (am, sw_if_index);
   return (NULL);
 }
 
@@ -871,7 +863,7 @@ VNET_SW_INTERFACE_ADD_DEL_FUNCTION (vnet_arp_add_del_sw_interface);
 const static ip_neighbor_vft_t arp_vft = {
   .inv_proxy4_add = arp_proxy_add,
   .inv_proxy4_del = arp_proxy_del,
-  .inv_proxy4_enable = arp_proxy_disable,
+  .inv_proxy4_enable = arp_proxy_enable,
   .inv_proxy4_disable = arp_proxy_disable,
 };
 
@@ -897,12 +889,39 @@ ethernet_arp_init (vlib_main_t * vm)
     vlib_node_runtime_t *rt =
       vlib_node_get_runtime (vm, arp_input_node.index);
 
-#define _(a,b)                                  \
-    vnet_pcap_drop_trace_filter_add_del         \
-        (rt->errors[ETHERNET_ARP_ERROR_##a],    \
-         1 /* is_add */);
-    foreach_ethernet_arp_error
-#undef _
+    vnet_pcap_drop_trace_filter_add_del (rt->errors[ARP_ERROR_REPLIES_SENT],
+                                        1);
+    vnet_pcap_drop_trace_filter_add_del (rt->errors[ARP_ERROR_DISABLED], 1);
+    vnet_pcap_drop_trace_filter_add_del (
+      rt->errors[ARP_ERROR_L2_TYPE_NOT_ETHERNET], 1);
+    vnet_pcap_drop_trace_filter_add_del (rt->errors[ARP_ERROR_L3_TYPE_NOT_IP4],
+                                        1);
+    vnet_pcap_drop_trace_filter_add_del (
+      rt->errors[ARP_ERROR_L3_SRC_ADDRESS_NOT_LOCAL], 1);
+    vnet_pcap_drop_trace_filter_add_del (
+      rt->errors[ARP_ERROR_L3_DST_ADDRESS_NOT_LOCAL], 1);
+    vnet_pcap_drop_trace_filter_add_del (
+      rt->errors[ARP_ERROR_L3_DST_ADDRESS_UNSET], 1);
+    vnet_pcap_drop_trace_filter_add_del (
+      rt->errors[ARP_ERROR_L3_SRC_ADDRESS_IS_LOCAL], 1);
+    vnet_pcap_drop_trace_filter_add_del (
+      rt->errors[ARP_ERROR_L3_SRC_ADDRESS_LEARNED], 1);
+    vnet_pcap_drop_trace_filter_add_del (
+      rt->errors[ARP_ERROR_REPLIES_RECEIVED], 1);
+    vnet_pcap_drop_trace_filter_add_del (
+      rt->errors[ARP_ERROR_OPCODE_NOT_REQUEST], 1);
+    vnet_pcap_drop_trace_filter_add_del (
+      rt->errors[ARP_ERROR_PROXY_ARP_REPLIES_SENT], 1);
+    vnet_pcap_drop_trace_filter_add_del (
+      rt->errors[ARP_ERROR_L2_ADDRESS_MISMATCH], 1);
+    vnet_pcap_drop_trace_filter_add_del (rt->errors[ARP_ERROR_GRATUITOUS_ARP],
+                                        1);
+    vnet_pcap_drop_trace_filter_add_del (
+      rt->errors[ARP_ERROR_INTERFACE_NO_TABLE], 1);
+    vnet_pcap_drop_trace_filter_add_del (
+      rt->errors[ARP_ERROR_INTERFACE_NOT_IP_ENABLED], 1);
+    vnet_pcap_drop_trace_filter_add_del (
+      rt->errors[ARP_ERROR_UNNUMBERED_MISMATCH], 1);
   }
 
   {
@@ -912,7 +931,7 @@ ethernet_arp_init (vlib_main_t * vm)
     vec_add1 (im->enable_disable_interface_callbacks, cb);
   }
 
-  ip_neighbor_register (IP46_TYPE_IP4, &arp_vft);
+  ip_neighbor_register (AF_IP4, &arp_vft);
 
   return 0;
 }