l2: check secondary macs on BVI interface

Type: fix

VRRP cannot be used on a BVI interface currently because packets sent
to the virtual mac address of the VR fail the destination mac check in
l2_to_bvi().

Apparently people want to use VRRP on BVI interfaces, so update the
check in l2_to_bvi() so that it will check any secondary mac addresses
which have been added to the ethernet interface if the destination mac
address does not match the primary mac address for the interface.

An equivalent check is already done in ethernet_input_inline() for L3
interfaces which are in promiscuous mode.

Change-Id: I7c5bf624dafda8744fea236c704e8e17e5f53b35
Signed-off-by: Matthew Smith <mgsmith@netgate.com>
diff --git a/src/vnet/l2/l2_bvi.h b/src/vnet/l2/l2_bvi.h
index d7d410b..300ea81 100644
--- a/src/vnet/l2/l2_bvi.h
+++ b/src/vnet/l2/l2_bvi.h
@@ -28,6 +28,27 @@
 #define TO_BVI_ERR_BAD_MAC   1
 #define TO_BVI_ERR_ETHERTYPE 2
 
+static_always_inline u32
+l2_to_bvi_dmac_check (vnet_hw_interface_t * hi, u8 * dmac,
+		      ethernet_interface_t * ei, u8 have_sec_dmac)
+{
+  mac_address_t *sec_addr;
+
+  if (ethernet_mac_address_equal (dmac, hi->hw_address))
+    return TO_BVI_ERR_OK;
+
+  if (have_sec_dmac)
+    {
+      vec_foreach (sec_addr, ei->secondary_addrs)
+      {
+	if (ethernet_mac_address_equal (dmac, (u8 *) sec_addr))
+	  return TO_BVI_ERR_OK;
+      }
+    }
+
+  return TO_BVI_ERR_BAD_MAC;
+}
+
 /**
  * Send a packet from L2 processing to L3 via the BVI interface.
  * Set next0 to the proper L3 input node.
@@ -40,14 +61,26 @@
 	   vlib_buffer_t * b0,
 	   u32 bvi_sw_if_index, next_by_ethertype_t * l3_next, u16 * next0)
 {
+  ethernet_main_t *em = &ethernet_main;
+
   /* Perform L3 my-mac filter */
   ethernet_header_t *e0 = vlib_buffer_get_current (b0);
   if (!ethernet_address_cast (e0->dst_address))
     {
       vnet_hw_interface_t *hi =
 	vnet_get_sup_hw_interface (vnet_main, bvi_sw_if_index);
-      if (!ethernet_mac_address_equal (e0->dst_address, hi->hw_address))
-	return TO_BVI_ERR_BAD_MAC;
+      ethernet_interface_t *ei = ethernet_get_interface (em, hi->hw_if_index);
+      u32 rv;
+
+      if (PREDICT_FALSE (vec_len (ei->secondary_addrs) > 0))
+	rv = l2_to_bvi_dmac_check (hi, e0->dst_address, ei,
+				   1 /* have_sec_dmac */ );
+      else
+	rv = l2_to_bvi_dmac_check (hi, e0->dst_address, ei,
+				   0 /* have_sec_dmac */ );
+
+      if (rv != TO_BVI_ERR_OK)
+	return rv;
     }
 
   /* Save L2 header position which may be changed due to packet replication */