L2 over MPLS
[vpp.git] / src / vnet / ethernet / arp.c
index 619628b..8a39400 100644 (file)
@@ -22,6 +22,7 @@
 #include <vnet/l2/l2_input.h>
 #include <vppinfra/mhash.h>
 #include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/fib_entry_src.h>
 #include <vnet/adj/adj_nbr.h>
 #include <vnet/adj/adj_mcast.h>
 #include <vnet/mpls/mpls.h>
@@ -109,6 +110,9 @@ typedef struct
 
 static const u8 vrrp_prefix[] = { 0x00, 0x00, 0x5E, 0x00, 0x01 };
 
+/* Node index for send_garp_na_process */
+u32 send_garp_na_process_node_index;
+
 static void
 set_ip4_over_ethernet_rpc_callback (vnet_arp_set_ip4_over_ethernet_rpc_args_t
                                    * a);
@@ -260,7 +264,7 @@ format_ethernet_arp_ip4_entry (u8 * s, va_list * va)
   if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_NO_FIB_ENTRY)
     flags = format (flags, "N");
 
-  s = format (s, "%=12U%=16U%=6s%=20U%=24U",
+  s = format (s, "%=12U%=16U%=6s%=20U%U",
              format_vlib_cpu_time, vnm->vlib_main, e->cpu_time_last_updated,
              format_ip4_address, &e->ip4_address,
              flags ? (char *) flags : "",
@@ -584,7 +588,7 @@ vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm,
          e->fib_entry_index =
            fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ,
                                      FIB_ENTRY_FLAG_ATTACHED,
-                                     FIB_PROTOCOL_IP4, &pfx.fp_addr,
+                                     DPO_PROTO_IP4, &pfx.fp_addr,
                                      e->sw_if_index, ~0, 1, NULL,
                                      FIB_ROUTE_PATH_FLAG_NONE);
        }
@@ -955,52 +959,118 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
                                          32);
          dst_flags = fib_entry_get_flags (dst_fei);
 
-         src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
-                                         &arp0->ip4_over_ethernet[0].ip4,
-                                         32);
-         src_flags = fib_entry_get_flags (src_fei);
-
          conn_sw_if_index0 = fib_entry_get_resolving_interface (dst_fei);
 
-         if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags))
-           {
-             error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
-             goto drop1;
-           }
-
          /* Honor unnumbered interface, if any */
          is_unnum0 = sw_if_index0 != conn_sw_if_index0;
 
-         /* Source must also be local to subnet of matching interface address. */
-         if (!((FIB_ENTRY_FLAG_ATTACHED & src_flags) ||
-               (FIB_ENTRY_FLAG_CONNECTED & src_flags)))
+         {
+           /*
+            * we're looking for FIB entries that indicate the source
+            * is attached. There may be more specific non-attached
+            * routes tht match the source, but these do not influence
+            * whether we respond to an ARP request, i.e. they do not
+            * influence whether we are the correct way for the sender
+            * to reach us, they only affect how we reach the sender.
+            */
+           fib_entry_t *src_fib_entry;
+           fib_entry_src_t *src;
+           fib_source_t source;
+           fib_prefix_t pfx;
+           int attached;
+           int mask;
+
+           mask = 32;
+           attached = 0;
+
+           do
+             {
+               src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
+                                               &arp0->
+                                               ip4_over_ethernet[0].ip4,
+                                               mask);
+               src_fib_entry = fib_entry_get (src_fei);
+
+               /*
+                * It's possible that the source that provides the
+                * flags we need, or the flags we must not have,
+                * is not the best source, so check then all.
+                */
+                /* *INDENT-OFF* */
+                FOR_EACH_SRC_ADDED(src_fib_entry, src, source,
+                ({
+                  src_flags = fib_entry_get_flags_for_source (src_fei, source);
+
+                  /* Reject requests/replies with our local interface
+                     address. */
+                  if (FIB_ENTRY_FLAG_LOCAL & src_flags)
+                    {
+                      error0 = ETHERNET_ARP_ERROR_l3_src_address_is_local;
+                      goto drop2;
+                    }
+                  /* 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* */
+
+               /*
+                * shorter mask lookup for the next iteration.
+                */
+               fib_entry_get_prefix (src_fei, &pfx);
+               mask = pfx.fp_len - 1;
+
+               /*
+                * continue until we hit the default route or we find
+                * the attached we are looking for. The most likely
+                * outcome is we find the attached with the first source
+                * on the first lookup.
+                */
+             }
+           while (!attached &&
+                  !fib_entry_is_sourced (src_fei, FIB_SOURCE_DEFAULT_ROUTE));
+
+           if (!attached)
+             {
+               /*
+                * the matching route is a not attached, i.e. it was
+                * added as a result of routing, rather than interface/ARP
+                * configuration. If the matching route is not a host route
+                * (i.e. a /32)
+                */
+               error0 = ETHERNET_ARP_ERROR_l3_src_address_not_local;
+               goto drop2;
+             }
+         }
+
+         if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags))
            {
-             /*
-              * 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 = ETHERNET_ARP_ERROR_l3_src_address_not_local;
-             goto drop2;
+             error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
+             goto drop1;
            }
 
          if (sw_if_index0 != fib_entry_get_resolving_interface (src_fei))
            {
              /*
               * The interface the ARP was received on is not the interface
-              * on which the covering prefix is configured. Maybe this is a case
-              * for unnumbered.
+              * on which the covering prefix is configured. Maybe this is a
+              * case for unnumbered.
               */
              is_unnum0 = 1;
            }
 
-         /* Reject requests/replies with our local interface address. */
-         if (FIB_ENTRY_FLAG_LOCAL & src_flags)
-           {
-             error0 = ETHERNET_ARP_ERROR_l3_src_address_is_local;
-             goto drop2;
-           }
-
          dst_is_local0 = (FIB_ENTRY_FLAG_LOCAL & dst_flags);
          fib_entry_get_prefix (dst_fei, &pfx0);
          if_addr0 = &pfx0.fp_addr.ip4;
@@ -1551,7 +1621,7 @@ arp_entry_free (ethernet_arp_interface_t * eai, ethernet_arp_ip4_entry_t * e)
 
       fib_table_entry_path_remove (fib_index, &pfx,
                                   FIB_SOURCE_ADJ,
-                                  FIB_PROTOCOL_IP4,
+                                  DPO_PROTO_IP4,
                                   &pfx.fp_addr,
                                   e->sw_if_index, ~0, 1,
                                   FIB_ROUTE_PATH_FLAG_NONE);
@@ -2221,12 +2291,8 @@ arp_term_l2bd (vlib_main_t * vm,
 
        next_l2_feature:
          {
-           u32 feature_bitmap0 =
-             vnet_buffer (p0)->l2.feature_bitmap & ~L2INPUT_FEAT_ARP_TERM;
-           vnet_buffer (p0)->l2.feature_bitmap = feature_bitmap0;
-           next0 =
-             feat_bitmap_get_next_node_index (arp_term_next_node_index,
-                                              feature_bitmap0);
+           next0 = vnet_l2_feature_next (p0, arp_term_next_node_index,
+                                         L2INPUT_FEAT_ARP_TERM);
            vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
                                             to_next, n_left_to_next,
                                             pi0, next0);
@@ -2311,6 +2377,86 @@ ethernet_arp_change_mac (u32 sw_if_index)
   /* *INDENT-ON* */
 }
 
+void static
+send_ip4_garp (vlib_main_t * vm, vnet_hw_interface_t * hi)
+{
+  ip4_main_t *i4m = &ip4_main;
+  u32 sw_if_index = hi->sw_if_index;
+  ip4_address_t *ip4_addr = ip4_interface_first_address (i4m, sw_if_index, 0);
+
+  if (ip4_addr)
+    {
+      clib_warning ("Sending GARP for IP4 address %U on sw_if_idex %d",
+                   format_ip4_address, ip4_addr, sw_if_index);
+
+      /* Form GARP packet for output - Gratuitous ARP is an ARP request packet
+         where the interface IP/MAC pair is used for both source and request
+         MAC/IP pairs in the request */
+      u32 bi = 0;
+      ethernet_arp_header_t *h = vlib_packet_template_get_packet
+       (vm, &i4m->ip4_arp_request_packet_template, &bi);
+      clib_memcpy (h->ip4_over_ethernet[0].ethernet, hi->hw_address,
+                  sizeof (h->ip4_over_ethernet[0].ethernet));
+      clib_memcpy (h->ip4_over_ethernet[1].ethernet, hi->hw_address,
+                  sizeof (h->ip4_over_ethernet[1].ethernet));
+      h->ip4_over_ethernet[0].ip4 = ip4_addr[0];
+      h->ip4_over_ethernet[1].ip4 = ip4_addr[0];
+
+      /* Setup MAC header with ARP Etype and broadcast DMAC */
+      vlib_buffer_t *b = vlib_get_buffer (vm, bi);
+      vlib_buffer_advance (b, -sizeof (ethernet_header_t));
+      ethernet_header_t *e = vlib_buffer_get_current (b);
+      e->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
+      clib_memcpy (e->src_address, hi->hw_address, sizeof (e->src_address));
+      memset (e->dst_address, 0xff, sizeof (e->dst_address));
+
+      /* Send GARP packet out the specified interface */
+      vnet_buffer (b)->sw_if_index[VLIB_RX] =
+       vnet_buffer (b)->sw_if_index[VLIB_TX] = sw_if_index;
+      vlib_frame_t *f = vlib_get_frame_to_node (vm, hi->output_node_index);
+      u32 *to_next = vlib_frame_vector_args (f);
+      to_next[0] = bi;
+      f->n_vectors = 1;
+      vlib_put_frame_to_node (vm, hi->output_node_index, f);
+    }
+}
+
+static vlib_node_registration_t send_garp_na_proc_node;
+
+static uword
+send_garp_na_process (vlib_main_t * vm,
+                     vlib_node_runtime_t * rt, vlib_frame_t * f)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  uword event_type, *event_data = 0;
+
+  send_garp_na_process_node_index = send_garp_na_proc_node.index;
+
+  while (1)
+    {
+      vlib_process_wait_for_event (vm);
+      event_type = vlib_process_get_events (vm, &event_data);
+      if ((event_type == SEND_GARP_NA) && (vec_len (event_data) >= 1))
+       {
+         u32 hw_if_index = event_data[0];
+         vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
+         send_ip4_garp (vm, hi);
+         send_ip6_na (vm, hi);
+       }
+      vec_reset_length (event_data);
+    }
+  return 0;
+}
+
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (send_garp_na_proc_node, static) = {
+    .function = send_garp_na_process,
+    .type = VLIB_NODE_TYPE_PROCESS,
+    .name = "send-garp-na-process",
+};
+/* *INDENT-ON* */
+
 /*
  * fd.io coding-style-patch-verification: ON
  *