docs: vnet comment nitfixes
[vpp.git] / src / vnet / ethernet / node.c
old mode 100755 (executable)
new mode 100644 (file)
index 87c447f..214e688
@@ -44,6 +44,7 @@
 #include <vnet/devices/pipe/pipe.h>
 #include <vppinfra/sparse_vec.h>
 #include <vnet/l2/l2_bvi.h>
+#include <vnet/classify/pcap_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,11 +194,18 @@ 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.
 static_always_inline void
-identify_subint (vnet_hw_interface_t * hi,
+identify_subint (ethernet_main_t * em,
+                vnet_hw_interface_t * hi,
                 vlib_buffer_t * b0,
                 u32 match_flags,
                 main_intf_t * main_intf,
@@ -206,28 +214,38 @@ identify_subint (vnet_hw_interface_t * hi,
                 u32 * new_sw_if_index, u8 * error0, u32 * is_l2)
 {
   u32 matched;
+  ethernet_interface_t *ei = ethernet_get_interface (em, hi->hw_if_index);
 
   matched = eth_identify_subint (hi, match_flags, main_intf, vlan_intf,
                                 qinq_intf, new_sw_if_index, error0, is_l2);
 
   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))
+      // A unicast packet arriving on an L3 interface must have a dmac
+      // matching the interface mac. If interface has STATUS_L3 bit set
+      // mac filter is already done.
+      if (!(*is_l2 || (ei->flags & ETHERNET_INTERFACE_FLAG_STATUS_L3)))
        {
+         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 +625,203 @@ 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 = ei->address.as_u64;
+  u8 bad = 0;
+
+  ASSERT (0 == ei->address.zero);
+
+  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))
+    {
+      ethernet_interface_address_t *sec_addr;
+
+      vec_foreach (sec_addr, ei->secondary_addrs)
+      {
+       ASSERT (0 == sec_addr->zero);
+       hwaddr = sec_addr->as_u64;
+
+       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 = ei->address.as_u64;
+  u64 *dmac = dmacs;
+  u8 *dmac_bad = dmacs_bad;
+  u32 bad = 0;
+  i32 n_left = n_packets;
+
+  ASSERT (0 == ei->address.zero);
+
+#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)
+    {
+      ethernet_interface_address_t *addr;
+
+      vec_foreach (addr, ei->secondary_addrs)
+      {
+       u64 hwaddr = addr->as_u64;
+       i32 n_left = n_packets;
+       u64 *dmac = dmacs;
+       u8 *dmac_bad = dmacs_bad;
+
+       ASSERT (0 == addr->zero);
+
+       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 +851,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 +880,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 +895,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;
+      dmac += 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;
@@ -931,29 +1094,35 @@ eth_input_single_int (vlib_main_t * vm, vlib_node_runtime_t * node,
   subint_config_t *subint0 = &intf0->untagged_subint;
 
   int main_is_l3 = (subint0->flags & SUBINT_CONFIG_L2) == 0;
-  int promisc = (ei->flags & ETHERNET_INTERFACE_FLAG_ACCEPT_ALL) != 0;
+  int int_is_l3 = ei->flags & ETHERNET_INTERFACE_FLAG_STATUS_L3;
 
   if (main_is_l3)
     {
-      /* main interface is L3, we dont expect tagged packets and interface
-         is not in promisc node, so we dont't need to check DMAC */
-      int is_l3 = 1;
-
-      if (promisc == 0)
-       eth_input_process_frame (vm, node, hi, from, n_pkts, is_l3,
-                                ip4_cksum_ok, 0);
+      if (int_is_l3 ||         /* DMAC filter already done by NIC */
+         ((hi->l2_if_count != 0) && (hi->l3_if_count == 0)))
+       {                       /* All L2 usage - DMAC check not needed */
+         eth_input_process_frame (vm, node, hi, from, n_pkts,
+                                  /*is_l3 */ 1, ip4_cksum_ok, 0);
+       }
       else
-       /* subinterfaces and promisc mode so DMAC check is needed */
-       eth_input_process_frame (vm, node, hi, from, n_pkts, is_l3,
-                                ip4_cksum_ok, 1);
+       {                       /* DMAC check needed for L3 */
+         eth_input_process_frame (vm, node, hi, from, n_pkts,
+                                  /*is_l3 */ 1, ip4_cksum_ok, 1);
+       }
       return;
     }
   else
     {
-      /* untagged packets are treated as L2 */
-      int is_l3 = 0;
-      eth_input_process_frame (vm, node, hi, from, n_pkts, is_l3,
-                              ip4_cksum_ok, 1);
+      if (hi->l3_if_count == 0)
+       {                       /* All L2 usage - DMAC check not needed */
+         eth_input_process_frame (vm, node, hi, from, n_pkts,
+                                  /*is_l3 */ 0, ip4_cksum_ok, 0);
+       }
+      else
+       {                       /* DMAC check needed for L3 */
+         eth_input_process_frame (vm, node, hi, from, n_pkts,
+                                  /*is_l3 */ 0, ip4_cksum_ok, 1);
+       }
       return;
     }
 }
@@ -962,6 +1131,7 @@ static_always_inline void
 ethernet_input_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
                      vlib_frame_t * from_frame)
 {
+  vnet_main_t *vnm = vnet_get_main ();
   u32 *from, n_left;
   if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
     {
@@ -990,9 +1160,10 @@ 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 (vnm->pcap.pcap_rx_enable))
     {
       u32 bi0;
+      vnet_pcap_t *pp = &vnm->pcap;
 
       from = vlib_frame_vector_args (from_frame);
       n_left = from_frame->n_vectors;
@@ -1001,15 +1172,10 @@ ethernet_input_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
          vlib_buffer_t *b0;
          bi0 = from[0];
          from++;
-         b0 = vlib_get_buffer (vm, bi0);
-
-         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])
-           {
-             pcap_add_buffer (&vm->pcap[VLIB_RX].pcap_main, vm, bi0, 512);
-           }
          n_left--;
+         b0 = vlib_get_buffer (vm, bi0);
+         if (vnet_is_packet_pcaped (pp, b0, ~0))
+           pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
        }
     }
 }
@@ -1029,6 +1195,7 @@ 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;
 
@@ -1066,6 +1233,8 @@ 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. */
          {
@@ -1123,6 +1292,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;
@@ -1145,14 +1315,31 @@ 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))
+                 if (ei && (ei->flags & ETHERNET_INTERFACE_FLAG_STATUS_L3))
+                   goto skip_dmac_check01;
+
+                 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;
+
+               skip_dmac_check01:
                  vlib_buffer_advance (b0, sizeof (ethernet_header_t));
                  determine_next_node (em, variant, 0, type0, b0,
                                       &error0, &next0);
@@ -1196,14 +1383,16 @@ ethernet_input_inline (vlib_main_t * vm,
                                  &hi1,
                                  &main_intf1, &vlan_intf1, &qinq_intf1);
 
-         identify_subint (hi0,
+         identify_subint (em,
+                          hi0,
                           b0,
                           match_flags0,
                           main_intf0,
                           vlan_intf0,
                           qinq_intf0, &new_sw_if_index0, &error0, &is_l20);
 
-         identify_subint (hi1,
+         identify_subint (em,
+                          hi1,
                           b1,
                           match_flags1,
                           main_intf1,
@@ -1309,12 +1498,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_prefetch_buffer_header (b[1], STORE);
-             CLIB_PREFETCH (b[1]->data, CLIB_CACHE_LINE_BYTES, LOAD);
+             clib_prefetch_load (b[1]->data);
            }
 
          bi0 = from[0];
@@ -1350,6 +1541,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;
@@ -1367,10 +1559,28 @@ 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))
+                 if (ei && ei->flags & ETHERNET_INTERFACE_FLAG_STATUS_L3)
+                   goto skip_dmac_check0;
+
+                 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;
+
+               skip_dmac_check0:
                  vlib_buffer_advance (b0, sizeof (ethernet_header_t));
                  determine_next_node (em, variant, 0, type0, b0,
                                       &error0, &next0);
@@ -1395,7 +1605,8 @@ ethernet_input_inline (vlib_main_t * vm,
                                  &hi0,
                                  &main_intf0, &vlan_intf0, &qinq_intf0);
 
-         identify_subint (hi0,
+         identify_subint (em,
+                          hi0,
                           b0,
                           match_flags0,
                           main_intf0,
@@ -1686,7 +1897,7 @@ ethernet_sw_interface_get_config (vnet_main_t * vnm,
            }
          else
            {
-             // a specific outer + specifc innner vlan id, a common case
+             // a specific outer + specific innner vlan id, a common case
 
              // get the qinq table
              if (vlan_table->vlans[si->sub.eth.outer_vlan_id].qinqs == 0)
@@ -1718,14 +1929,14 @@ static clib_error_t *
 ethernet_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
 {
   subint_config_t *subint;
-  u32 dummy_flags;
-  u32 dummy_unsup;
+  u32 placeholder_flags;
+  u32 placeholder_unsup;
   clib_error_t *error = 0;
 
   // Find the config for this subinterface
   subint =
-    ethernet_sw_interface_get_config (vnm, sw_if_index, &dummy_flags,
-                                     &dummy_unsup);
+    ethernet_sw_interface_get_config (vnm, sw_if_index, &placeholder_flags,
+                                     &placeholder_unsup);
 
   if (subint == 0)
     {
@@ -1749,8 +1960,8 @@ void
 ethernet_sw_interface_set_l2_mode (vnet_main_t * vnm, u32 sw_if_index, u32 l2)
 {
   subint_config_t *subint;
-  u32 dummy_flags;
-  u32 dummy_unsup;
+  u32 placeholder_flags;
+  u32 placeholder_unsup;
   int is_port;
   vnet_sw_interface_t *sw = vnet_get_sw_interface (vnm, sw_if_index);
 
@@ -1758,8 +1969,8 @@ ethernet_sw_interface_set_l2_mode (vnet_main_t * vnm, u32 sw_if_index, u32 l2)
 
   // Find the config for this subinterface
   subint =
-    ethernet_sw_interface_get_config (vnm, sw_if_index, &dummy_flags,
-                                     &dummy_unsup);
+    ethernet_sw_interface_get_config (vnm, sw_if_index, &placeholder_flags,
+                                     &placeholder_unsup);
 
   if (subint == 0)
     {
@@ -1799,13 +2010,13 @@ ethernet_sw_interface_set_l2_mode_noport (vnet_main_t * vnm,
                                          u32 sw_if_index, u32 l2)
 {
   subint_config_t *subint;
-  u32 dummy_flags;
-  u32 dummy_unsup;
+  u32 placeholder_flags;
+  u32 placeholder_unsup;
 
   /* Find the config for this subinterface */
   subint =
-    ethernet_sw_interface_get_config (vnm, sw_if_index, &dummy_flags,
-                                     &dummy_unsup);
+    ethernet_sw_interface_get_config (vnm, sw_if_index, &placeholder_flags,
+                                     &placeholder_unsup);
 
   if (subint == 0)
     {
@@ -2025,11 +2236,20 @@ next_by_ethertype_register (next_by_ethertype_t * l3_next,
   return 0;
 }
 
+void
+ethernet_setup_node (vlib_main_t *vm, u32 node_index)
+{
+  vlib_node_t *n = vlib_get_node (vm, node_index);
+  pg_node_t *pn = pg_get_node (node_index);
+
+  n->format_buffer = format_ethernet_header_with_length;
+  n->unformat_buffer = unformat_ethernet_header;
+  pn->unformat_edit = unformat_pg_ethernet_header;
+}
 
-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;
 
@@ -2048,12 +2268,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)
@@ -2069,6 +2285,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);