ethernet: add sanity checks to p2p_ethernet_add/del
[vpp.git] / src / vnet / ethernet / node.c
index 950a962..5136da4 100755 (executable)
@@ -44,6 +44,7 @@
 #include <vnet/devices/pipe/pipe.h>
 #include <vppinfra/sparse_vec.h>
 #include <vnet/l2/l2_bvi.h>
+#include <vnet/classify/trace_classify.h>
 
 #define foreach_ethernet_input_next            \
   _ (PUNT, "error-punt")                       \
@@ -113,7 +114,7 @@ parse_header (ethernet_input_variant_t variant,
     {
       ethernet_header_t *e0;
 
-      e0 = (void *) (b0->data + b0->current_data);
+      e0 = vlib_buffer_get_current (b0);
 
       vnet_buffer (b0)->l2_hdr_offset = b0->current_data;
       b0->flags |= VNET_BUFFER_F_L2_HDR_OFFSET_VALID;
@@ -127,7 +128,7 @@ parse_header (ethernet_input_variant_t variant,
       // here when prior node was LLC/SNAP processing
       u16 *e0;
 
-      e0 = (void *) (b0->data + b0->current_data);
+      e0 = vlib_buffer_get_current (b0);
 
       vlib_buffer_advance (b0, sizeof (e0[0]));
 
@@ -152,7 +153,7 @@ parse_header (ethernet_input_variant_t variant,
 
       *match_flags = SUBINT_CONFIG_VALID | SUBINT_CONFIG_MATCH_1_TAG;
 
-      h0 = (void *) (b0->data + b0->current_data);
+      h0 = vlib_buffer_get_current (b0);
 
       tag = clib_net_to_host_u16 (h0->priority_cfi_and_id);
 
@@ -170,7 +171,7 @@ parse_header (ethernet_input_variant_t variant,
          // Double tagged packet
          *match_flags = SUBINT_CONFIG_VALID | SUBINT_CONFIG_MATCH_2_TAG;
 
-         h0 = (void *) (b0->data + b0->current_data);
+         h0 = vlib_buffer_get_current (b0);
 
          tag = clib_net_to_host_u16 (h0->priority_cfi_and_id);
 
@@ -193,6 +194,12 @@ parse_header (ethernet_input_variant_t variant,
   ethernet_buffer_set_vlan_count (b0, vlan_count);
 }
 
+static_always_inline void
+ethernet_input_inline_dmac_check (vnet_hw_interface_t * hi,
+                                 u64 * dmacs, u8 * dmacs_bad,
+                                 u32 n_packets, ethernet_interface_t * ei,
+                                 u8 have_sec_dmac);
+
 // Determine the subinterface for this packet, given the result of the
 // vlan table lookups and vlan header parsing. Check the most specific
 // matches first.
@@ -212,22 +219,31 @@ identify_subint (vnet_hw_interface_t * hi,
 
   if (matched)
     {
-
       // Perform L3 my-mac filter
       // A unicast packet arriving on an L3 interface must have a dmac matching the interface mac.
       // This is required for promiscuous mode, else we will forward packets we aren't supposed to.
       if (!(*is_l2))
        {
+         u64 dmacs[2];
+         u8 dmacs_bad[2];
          ethernet_header_t *e0;
+         ethernet_interface_t *ei0;
+
          e0 = (void *) (b0->data + vnet_buffer (b0)->l2_hdr_offset);
+         dmacs[0] = *(u64 *) e0;
+         ei0 = ethernet_get_interface (&ethernet_main, hi->hw_if_index);
 
-         if (!(ethernet_address_cast (e0->dst_address)))
-           {
-             if (!ethernet_mac_address_equal ((u8 *) e0, hi->hw_address))
-               {
-                 *error0 = ETHERNET_ERROR_L3_MAC_MISMATCH;
-               }
-           }
+         if (ei0 && vec_len (ei0->secondary_addrs))
+           ethernet_input_inline_dmac_check (hi, dmacs, dmacs_bad,
+                                             1 /* n_packets */ , ei0,
+                                             1 /* have_sec_dmac */ );
+         else
+           ethernet_input_inline_dmac_check (hi, dmacs, dmacs_bad,
+                                             1 /* n_packets */ , ei0,
+                                             0 /* have_sec_dmac */ );
+
+         if (dmacs_bad[0])
+           *error0 = ETHERNET_ERROR_L3_MAC_MISMATCH;
        }
 
       // Check for down subinterface
@@ -607,6 +623,196 @@ eth_input_tag_lookup (vlib_main_t * vm, vnet_main_t * vnm,
   l->n_bytes += vlib_buffer_length_in_chain (vm, b);
 }
 
+#define DMAC_MASK clib_net_to_host_u64 (0xFFFFFFFFFFFF0000)
+#define DMAC_IGBIT clib_net_to_host_u64 (0x0100000000000000)
+
+#ifdef CLIB_HAVE_VEC256
+static_always_inline u32
+is_dmac_bad_x4 (u64 * dmacs, u64 hwaddr)
+{
+  u64x4 r0 = u64x4_load_unaligned (dmacs) & u64x4_splat (DMAC_MASK);
+  r0 = (r0 != u64x4_splat (hwaddr)) & ((r0 & u64x4_splat (DMAC_IGBIT)) == 0);
+  return u8x32_msb_mask ((u8x32) (r0));
+}
+#endif
+
+static_always_inline u8
+is_dmac_bad (u64 dmac, u64 hwaddr)
+{
+  u64 r0 = dmac & DMAC_MASK;
+  return (r0 != hwaddr) && ((r0 & DMAC_IGBIT) == 0);
+}
+
+static_always_inline u8
+is_sec_dmac_bad (u64 dmac, u64 hwaddr)
+{
+  return ((dmac & DMAC_MASK) != hwaddr);
+}
+
+#ifdef CLIB_HAVE_VEC256
+static_always_inline u32
+is_sec_dmac_bad_x4 (u64 * dmacs, u64 hwaddr)
+{
+  u64x4 r0 = u64x4_load_unaligned (dmacs) & u64x4_splat (DMAC_MASK);
+  r0 = (r0 != u64x4_splat (hwaddr));
+  return u8x32_msb_mask ((u8x32) (r0));
+}
+#endif
+
+static_always_inline u8
+eth_input_sec_dmac_check_x1 (u64 hwaddr, u64 * dmac, u8 * dmac_bad)
+{
+  dmac_bad[0] &= is_sec_dmac_bad (dmac[0], hwaddr);
+  return dmac_bad[0];
+}
+
+static_always_inline u32
+eth_input_sec_dmac_check_x4 (u64 hwaddr, u64 * dmac, u8 * dmac_bad)
+{
+#ifdef CLIB_HAVE_VEC256
+  *(u32 *) (dmac_bad + 0) &= is_sec_dmac_bad_x4 (dmac + 0, hwaddr);
+#else
+  dmac_bad[0] &= is_sec_dmac_bad (dmac[0], hwaddr);
+  dmac_bad[1] &= is_sec_dmac_bad (dmac[1], hwaddr);
+  dmac_bad[2] &= is_sec_dmac_bad (dmac[2], hwaddr);
+  dmac_bad[3] &= is_sec_dmac_bad (dmac[3], hwaddr);
+#endif
+  return *(u32 *) dmac_bad;
+}
+
+/*
+ * DMAC check for ethernet_input_inline()
+ *
+ * dmacs and dmacs_bad are arrays that are 2 elements long
+ * n_packets should be 1 or 2 for ethernet_input_inline()
+ */
+static_always_inline void
+ethernet_input_inline_dmac_check (vnet_hw_interface_t * hi,
+                                 u64 * dmacs, u8 * dmacs_bad,
+                                 u32 n_packets, ethernet_interface_t * ei,
+                                 u8 have_sec_dmac)
+{
+  u64 hwaddr = (*(u64 *) hi->hw_address) & DMAC_MASK;
+  u8 bad = 0;
+
+  dmacs_bad[0] = is_dmac_bad (dmacs[0], hwaddr);
+  dmacs_bad[1] = ((n_packets > 1) & is_dmac_bad (dmacs[1], hwaddr));
+
+  bad = dmacs_bad[0] | dmacs_bad[1];
+
+  if (PREDICT_FALSE (bad && have_sec_dmac))
+    {
+      mac_address_t *sec_addr;
+
+      vec_foreach (sec_addr, ei->secondary_addrs)
+      {
+       hwaddr = (*(u64 *) sec_addr) & DMAC_MASK;
+
+       bad = (eth_input_sec_dmac_check_x1 (hwaddr, dmacs, dmacs_bad) |
+              eth_input_sec_dmac_check_x1 (hwaddr, dmacs + 1,
+                                           dmacs_bad + 1));
+
+       if (!bad)
+         return;
+      }
+    }
+}
+
+static_always_inline void
+eth_input_process_frame_dmac_check (vnet_hw_interface_t * hi,
+                                   u64 * dmacs, u8 * dmacs_bad,
+                                   u32 n_packets, ethernet_interface_t * ei,
+                                   u8 have_sec_dmac)
+{
+  u64 hwaddr = (*(u64 *) hi->hw_address) & DMAC_MASK;
+  u64 *dmac = dmacs;
+  u8 *dmac_bad = dmacs_bad;
+  u32 bad = 0;
+  i32 n_left = n_packets;
+
+#ifdef CLIB_HAVE_VEC256
+  while (n_left > 0)
+    {
+      bad |= *(u32 *) (dmac_bad + 0) = is_dmac_bad_x4 (dmac + 0, hwaddr);
+      bad |= *(u32 *) (dmac_bad + 4) = is_dmac_bad_x4 (dmac + 4, hwaddr);
+
+      /* next */
+      dmac += 8;
+      dmac_bad += 8;
+      n_left -= 8;
+    }
+#else
+  while (n_left > 0)
+    {
+      bad |= dmac_bad[0] = is_dmac_bad (dmac[0], hwaddr);
+      bad |= dmac_bad[1] = is_dmac_bad (dmac[1], hwaddr);
+      bad |= dmac_bad[2] = is_dmac_bad (dmac[2], hwaddr);
+      bad |= dmac_bad[3] = is_dmac_bad (dmac[3], hwaddr);
+
+      /* next */
+      dmac += 4;
+      dmac_bad += 4;
+      n_left -= 4;
+    }
+#endif
+
+  if (have_sec_dmac && bad)
+    {
+      mac_address_t *addr;
+
+      vec_foreach (addr, ei->secondary_addrs)
+      {
+       u64 hwaddr = ((u64 *) addr)[0] & DMAC_MASK;
+       i32 n_left = n_packets;
+       u64 *dmac = dmacs;
+       u8 *dmac_bad = dmacs_bad;
+
+       bad = 0;
+
+       while (n_left > 0)
+         {
+           int adv = 0;
+           int n_bad;
+
+           /* skip any that have already matched */
+           if (!dmac_bad[0])
+             {
+               dmac += 1;
+               dmac_bad += 1;
+               n_left -= 1;
+               continue;
+             }
+
+           n_bad = clib_min (4, n_left);
+
+           /* If >= 4 left, compare 4 together */
+           if (n_bad == 4)
+             {
+               bad |= eth_input_sec_dmac_check_x4 (hwaddr, dmac, dmac_bad);
+               adv = 4;
+               n_bad = 0;
+             }
+
+           /* handle individually */
+           while (n_bad > 0)
+             {
+               bad |= eth_input_sec_dmac_check_x1 (hwaddr, dmac + adv,
+                                                   dmac_bad + adv);
+               adv += 1;
+               n_bad -= 1;
+             }
+
+           dmac += adv;
+           dmac_bad += adv;
+           n_left -= adv;
+         }
+
+       if (!bad)               /* can stop looping if everything matched */
+         break;
+      }
+    }
+}
+
 /* process frame of buffers, store ethertype into array and update
    buffer metadata fields depending on interface being l2 or l3 assuming that
    packets are untagged. For tagged packets those fields are updated later.
@@ -636,17 +842,15 @@ eth_input_process_frame (vlib_main_t * vm, vlib_node_runtime_t * node,
   u16 et_vlan = clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
   u16 et_dot1ad = clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD);
   i32 n_left = n_packets;
-  vlib_buffer_t *b[20];
-  u32 *from;
+  vlib_buffer_t *bufs[VLIB_FRAME_SIZE];
+  vlib_buffer_t **b = bufs;
+  ethernet_interface_t *ei = ethernet_get_interface (em, hi->hw_if_index);
 
-  from = buffer_indices;
+  vlib_get_buffers (vm, buffer_indices, b, n_left);
 
   while (n_left >= 20)
     {
       vlib_buffer_t **ph = b + 16, **pd = b + 8;
-      vlib_get_buffers (vm, from, b, 4);
-      vlib_get_buffers (vm, from + 8, pd, 4);
-      vlib_get_buffers (vm, from + 16, ph, 4);
 
       vlib_prefetch_buffer_header (ph[0], LOAD);
       vlib_prefetch_buffer_data (pd[0], LOAD);
@@ -667,15 +871,14 @@ eth_input_process_frame (vlib_main_t * vm, vlib_node_runtime_t * node,
       eth_input_adv_and_flags_x4 (b, main_is_l3);
 
       /* next */
+      b += 4;
       n_left -= 4;
       etype += 4;
       tag += 4;
       dmac += 4;
-      from += 4;
     }
   while (n_left >= 4)
     {
-      vlib_get_buffers (vm, from, b, 4);
       eth_input_get_etype_and_tags (b, etype, tag, dmac, 0, dmac_check);
       eth_input_get_etype_and_tags (b, etype, tag, dmac, 1, dmac_check);
       eth_input_get_etype_and_tags (b, etype, tag, dmac, 2, dmac_check);
@@ -683,82 +886,33 @@ eth_input_process_frame (vlib_main_t * vm, vlib_node_runtime_t * node,
       eth_input_adv_and_flags_x4 (b, main_is_l3);
 
       /* next */
+      b += 4;
       n_left -= 4;
       etype += 4;
       tag += 4;
       dmac += 4;
-      from += 4;
     }
   while (n_left)
     {
-      vlib_get_buffers (vm, from, b, 1);
       eth_input_get_etype_and_tags (b, etype, tag, dmac, 0, dmac_check);
       eth_input_adv_and_flags_x1 (b, main_is_l3);
 
       /* next */
+      b += 1;
       n_left -= 1;
       etype += 1;
       tag += 1;
       dmac += 4;
-      from += 1;
     }
 
   if (dmac_check)
     {
-      u64 mask = clib_net_to_host_u64 (0xFFFFFFFFFFFF0000);
-      u64 igbit = clib_net_to_host_u64 (0x0100000000000000);
-      u64 hwaddr = (*(u64 *) hi->hw_address) & mask;
-      u64 *dmac = dmacs;
-      u8 *dmac_bad = dmacs_bad;
-
-      n_left = n_packets;
-
-#ifdef CLIB_HAVE_VEC256
-      u64x4 igbit4 = u64x4_splat (igbit);
-      u64x4 mask4 = u64x4_splat (mask);
-      u64x4 hwaddr4 = u64x4_splat (hwaddr);
-      while (n_left >= 0)
-       {
-         u64x4 r0, r1;
-         r0 = u64x4_load_unaligned (dmac + 0) & mask4;
-         r1 = u64x4_load_unaligned (dmac + 4) & mask4;
-
-         r0 = (r0 != hwaddr4) & ((r0 & igbit4) == 0);
-         r1 = (r1 != hwaddr4) & ((r1 & igbit4) == 0);
-
-         *(u32 *) (dmac_bad + 0) = u8x32_msb_mask ((u8x32) (r0));
-         *(u32 *) (dmac_bad + 4) = u8x32_msb_mask ((u8x32) (r1));
-
-         /* next */
-         dmac += 8;
-         dmac_bad += 8;
-         n_left -= 8;
-       }
-#else
-      while (n_left > 0)
-       {
-         u64 r0, r1, r2, r3;
-         r0 = dmac[0] & mask;
-         r1 = dmac[1] & mask;
-         r2 = dmac[2] & mask;
-         r3 = dmac[3] & mask;
-
-         r0 = (r0 != hwaddr) && ((r0 & igbit) == 0);
-         r1 = (r1 != hwaddr) && ((r1 & igbit) == 0);
-         r2 = (r2 != hwaddr) && ((r2 & igbit) == 0);
-         r3 = (r3 != hwaddr) && ((r3 & igbit) == 0);
-
-         dmac_bad[0] = r0;
-         dmac_bad[1] = r1;
-         dmac_bad[2] = r2;
-         dmac_bad[3] = r3;
-
-         /* next */
-         dmac += 4;
-         dmac_bad += 4;
-         n_left -= 4;
-       }
-#endif
+      if (ei && vec_len (ei->secondary_addrs))
+       eth_input_process_frame_dmac_check (hi, dmacs, dmacs_bad, n_packets,
+                                           ei, 1 /* have_sec_dmac */ );
+      else
+       eth_input_process_frame_dmac_check (hi, dmacs, dmacs_bad, n_packets,
+                                           ei, 0 /* have_sec_dmac */ );
     }
 
   next_ip4 = em->l3_next.input_next_ip4;
@@ -963,7 +1117,7 @@ ethernet_input_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
                      vlib_frame_t * from_frame)
 {
   u32 *from, n_left;
-  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) == 0))
+  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
     {
       from = vlib_frame_vector_args (from_frame);
       n_left = from_frame->n_vectors;
@@ -990,26 +1144,48 @@ ethernet_input_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
     }
 
   /* rx pcap capture if enabled */
-  if (PREDICT_FALSE (vm->pcap[VLIB_RX].pcap_enable))
+  if (PREDICT_FALSE (vlib_global_main.pcap.pcap_rx_enable))
     {
       u32 bi0;
+      vnet_pcap_t *pp = &vlib_global_main.pcap;
 
       from = vlib_frame_vector_args (from_frame);
       n_left = from_frame->n_vectors;
       while (n_left > 0)
        {
+         int classify_filter_result;
          vlib_buffer_t *b0;
          bi0 = from[0];
          from++;
+         n_left--;
          b0 = vlib_get_buffer (vm, bi0);
+         if (pp->filter_classify_table_index != ~0)
+           {
+             classify_filter_result =
+               vnet_is_packet_traced_inline
+               (b0, pp->filter_classify_table_index, 0 /* full classify */ );
+             if (classify_filter_result)
+               pcap_add_buffer (&pp->pcap_main, vm, bi0,
+                                pp->max_bytes_per_pkt);
+             continue;
+           }
 
-         if (vm->pcap[VLIB_RX].pcap_sw_if_index == 0 ||
-             vm->pcap[VLIB_RX].pcap_sw_if_index
-             == vnet_buffer (b0)->sw_if_index[VLIB_RX])
+         if (pp->pcap_sw_if_index == 0 ||
+             pp->pcap_sw_if_index == vnet_buffer (b0)->sw_if_index[VLIB_RX])
            {
-             pcap_add_buffer (&vm->pcap[VLIB_RX].pcap_main, vm, bi0, 512);
+             vnet_main_t *vnm = vnet_get_main ();
+             vnet_hw_interface_t *hi =
+               vnet_get_sup_hw_interface
+               (vnm, vnet_buffer (b0)->sw_if_index[VLIB_RX]);
+
+             /* Capture pkt if not filtered, or if filter hits */
+             if (hi->trace_classify_table_index == ~0 ||
+                 vnet_is_packet_traced_inline
+                 (b0, hi->trace_classify_table_index,
+                  0 /* full classify */ ))
+               pcap_add_buffer (&pp->pcap_main, vm, bi0,
+                                pp->max_bytes_per_pkt);
            }
-         n_left--;
        }
     }
 }
@@ -1029,6 +1205,9 @@ ethernet_input_inline (vlib_main_t * vm,
   u32 cached_sw_if_index = ~0;
   u32 cached_is_l2 = 0;                /* shut up gcc */
   vnet_hw_interface_t *hi = NULL;      /* used for main interface only */
+  ethernet_interface_t *ei = NULL;
+  vlib_buffer_t *bufs[VLIB_FRAME_SIZE];
+  vlib_buffer_t **b = bufs;
 
   if (variant != ETHERNET_INPUT_VARIANT_ETHERNET)
     error_node = vlib_node_get_runtime (vm, ethernet_input_node.index);
@@ -1040,6 +1219,7 @@ ethernet_input_inline (vlib_main_t * vm,
   next_index = node->cached_next_index;
   stats_sw_if_index = node->runtime_data[0];
   stats_n_packets = stats_n_bytes = 0;
+  vlib_get_buffers (vm, from, bufs, n_left_from);
 
   while (n_left_from > 0)
     {
@@ -1063,19 +1243,16 @@ ethernet_input_inline (vlib_main_t * vm,
          qinq_intf_t *qinq_intf0, *qinq_intf1;
          u32 is_l20, is_l21;
          ethernet_header_t *e0, *e1;
+         u64 dmacs[2];
+         u8 dmacs_bad[2];
 
          /* Prefetch next iteration. */
          {
-           vlib_buffer_t *b2, *b3;
+           vlib_prefetch_buffer_header (b[2], STORE);
+           vlib_prefetch_buffer_header (b[3], STORE);
 
-           b2 = vlib_get_buffer (vm, from[2]);
-           b3 = vlib_get_buffer (vm, from[3]);
-
-           vlib_prefetch_buffer_header (b2, STORE);
-           vlib_prefetch_buffer_header (b3, STORE);
-
-           CLIB_PREFETCH (b2->data, sizeof (ethernet_header_t), LOAD);
-           CLIB_PREFETCH (b3->data, sizeof (ethernet_header_t), LOAD);
+           CLIB_PREFETCH (b[2]->data, sizeof (ethernet_header_t), LOAD);
+           CLIB_PREFETCH (b[3]->data, sizeof (ethernet_header_t), LOAD);
          }
 
          bi0 = from[0];
@@ -1087,8 +1264,9 @@ ethernet_input_inline (vlib_main_t * vm,
          n_left_to_next -= 2;
          n_left_from -= 2;
 
-         b0 = vlib_get_buffer (vm, bi0);
-         b1 = vlib_get_buffer (vm, bi1);
+         b0 = b[0];
+         b1 = b[1];
+         b += 2;
 
          error0 = error1 = ETHERNET_ERROR_NONE;
          e0 = vlib_buffer_get_current (b0);
@@ -1124,6 +1302,7 @@ ethernet_input_inline (vlib_main_t * vm,
                {
                  cached_sw_if_index = sw_if_index0;
                  hi = vnet_get_sup_hw_interface (vnm, sw_if_index0);
+                 ei = ethernet_get_interface (em, hi->hw_if_index);
                  intf0 = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
                  subint0 = &intf0->untagged_subint;
                  cached_is_l2 = is_l20 = subint0->flags & SUBINT_CONFIG_L2;
@@ -1146,14 +1325,27 @@ ethernet_input_inline (vlib_main_t * vm,
                }
              else
                {
-                 if (!ethernet_address_cast (e0->dst_address) &&
-                     (hi->hw_address != 0) &&
-                     !ethernet_mac_address_equal ((u8 *) e0, hi->hw_address))
+                 dmacs[0] = *(u64 *) e0;
+                 dmacs[1] = *(u64 *) e1;
+
+                 if (ei && vec_len (ei->secondary_addrs))
+                   ethernet_input_inline_dmac_check (hi, dmacs,
+                                                     dmacs_bad,
+                                                     2 /* n_packets */ ,
+                                                     ei,
+                                                     1 /* have_sec_dmac */ );
+                 else
+                   ethernet_input_inline_dmac_check (hi, dmacs,
+                                                     dmacs_bad,
+                                                     2 /* n_packets */ ,
+                                                     ei,
+                                                     0 /* have_sec_dmac */ );
+
+                 if (dmacs_bad[0])
                    error0 = ETHERNET_ERROR_L3_MAC_MISMATCH;
-                 if (!ethernet_address_cast (e1->dst_address) &&
-                     (hi->hw_address != 0) &&
-                     !ethernet_mac_address_equal ((u8 *) e1, hi->hw_address))
+                 if (dmacs_bad[1])
                    error1 = ETHERNET_ERROR_L3_MAC_MISMATCH;
+
                  vlib_buffer_advance (b0, sizeof (ethernet_header_t));
                  determine_next_node (em, variant, 0, type0, b0,
                                       &error0, &next0);
@@ -1310,15 +1502,14 @@ ethernet_input_inline (vlib_main_t * vm,
          qinq_intf_t *qinq_intf0;
          ethernet_header_t *e0;
          u32 is_l20;
+         u64 dmacs[2];
+         u8 dmacs_bad[2];
 
          // Prefetch next iteration
          if (n_left_from > 1)
            {
-             vlib_buffer_t *p2;
-
-             p2 = vlib_get_buffer (vm, from[1]);
-             vlib_prefetch_buffer_header (p2, STORE);
-             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD);
+             vlib_prefetch_buffer_header (b[1], STORE);
+             CLIB_PREFETCH (b[1]->data, CLIB_CACHE_LINE_BYTES, LOAD);
            }
 
          bi0 = from[0];
@@ -1328,7 +1519,8 @@ ethernet_input_inline (vlib_main_t * vm,
          n_left_from -= 1;
          n_left_to_next -= 1;
 
-         b0 = vlib_get_buffer (vm, bi0);
+         b0 = b[0];
+         b += 1;
 
          error0 = ETHERNET_ERROR_NONE;
          e0 = vlib_buffer_get_current (b0);
@@ -1353,6 +1545,7 @@ ethernet_input_inline (vlib_main_t * vm,
                {
                  cached_sw_if_index = sw_if_index0;
                  hi = vnet_get_sup_hw_interface (vnm, sw_if_index0);
+                 ei = ethernet_get_interface (em, hi->hw_if_index);
                  intf0 = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
                  subint0 = &intf0->untagged_subint;
                  cached_is_l2 = is_l20 = subint0->flags & SUBINT_CONFIG_L2;
@@ -1370,10 +1563,24 @@ ethernet_input_inline (vlib_main_t * vm,
                }
              else
                {
-                 if (!ethernet_address_cast (e0->dst_address) &&
-                     (hi->hw_address != 0) &&
-                     !ethernet_mac_address_equal ((u8 *) e0, hi->hw_address))
+                 dmacs[0] = *(u64 *) e0;
+
+                 if (ei && vec_len (ei->secondary_addrs))
+                   ethernet_input_inline_dmac_check (hi, dmacs,
+                                                     dmacs_bad,
+                                                     1 /* n_packets */ ,
+                                                     ei,
+                                                     1 /* have_sec_dmac */ );
+                 else
+                   ethernet_input_inline_dmac_check (hi, dmacs,
+                                                     dmacs_bad,
+                                                     1 /* n_packets */ ,
+                                                     ei,
+                                                     0 /* have_sec_dmac */ );
+
+                 if (dmacs_bad[0])
                    error0 = ETHERNET_ERROR_L3_MAC_MISMATCH;
+
                  vlib_buffer_advance (b0, sizeof (ethernet_header_t));
                  determine_next_node (em, variant, 0, type0, b0,
                                       &error0, &next0);
@@ -2028,11 +2235,9 @@ next_by_ethertype_register (next_by_ethertype_t * l3_next,
   return 0;
 }
 
-
-static clib_error_t *
-ethernet_input_init (vlib_main_t * vm)
+void
+ethernet_input_init (vlib_main_t * vm, ethernet_main_t * em)
 {
-  ethernet_main_t *em = &ethernet_main;
   __attribute__ ((unused)) vlan_table_t *invalid_vlan_table;
   __attribute__ ((unused)) qinq_table_t *invalid_qinq_table;
 
@@ -2051,12 +2256,8 @@ ethernet_input_init (vlib_main_t * vm)
   pool_get (em->vlan_pool, invalid_vlan_table);        // first id = 0
   // The first qinq pool will always be reserved for an invalid table
   pool_get (em->qinq_pool, invalid_qinq_table);        // first id = 0
-
-  return 0;
 }
 
-VLIB_INIT_FUNCTION (ethernet_input_init);
-
 void
 ethernet_register_input_type (vlib_main_t * vm,
                              ethernet_type_t type, u32 node_index)
@@ -2072,6 +2273,11 @@ ethernet_register_input_type (vlib_main_t * vm,
   }
 
   ti = ethernet_get_type_info (em, type);
+  if (ti == 0)
+    {
+      clib_warning ("type_info NULL for type %d", type);
+      return;
+    }
   ti->node_index = node_index;
   ti->next_index = vlib_node_add_next (vm,
                                       ethernet_input_node.index, node_index);