L2 BD: introduce a BD interface on which to send UU packets

Change-Id: I21ad6b04c19c8735d057174b1f260a59f2812241
Signed-off-by: Neale Ranns <nranns@cisco.com>
diff --git a/test/test_acl_plugin_l2l3.py b/test/test_acl_plugin_l2l3.py
index 26b562e..66d053f 100644
--- a/test/test_acl_plugin_l2l3.py
+++ b/test/test_acl_plugin_l2l3.py
@@ -36,6 +36,7 @@
 from scapy.layers.inet6 import IPv6ExtHdrFragment
 
 from framework import VppTestCase, VppTestRunner
+from vpp_papi_provider import L2_PORT_TYPE
 import time
 
 
@@ -70,7 +71,8 @@
 
         # Create BD with MAC learning enabled and put interfaces to this BD
         cls.vapi.sw_interface_set_l2_bridge(
-            cls.loop0.sw_if_index, bd_id=cls.bd_id, bvi=1)
+            cls.loop0.sw_if_index, bd_id=cls.bd_id,
+            port_type=L2_PORT_TYPE.BVI)
         cls.vapi.sw_interface_set_l2_bridge(
             cls.pg0.sw_if_index, bd_id=cls.bd_id)
         cls.vapi.sw_interface_set_l2_bridge(
diff --git a/test/test_acl_plugin_macip.py b/test/test_acl_plugin_macip.py
index f35db55..611bc73 100644
--- a/test/test_acl_plugin_macip.py
+++ b/test/test_acl_plugin_macip.py
@@ -16,6 +16,7 @@
 from vpp_lo_interface import VppLoInterface
 from vpp_papi_provider import L2_VTR_OP
 from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint
+from vpp_papi_provider import L2_PORT_TYPE
 
 
 class MethodHolder(VppTestCase):
@@ -90,7 +91,8 @@
 
             # Create BD with MAC learning enabled and put interfaces to this BD
             cls.vapi.sw_interface_set_l2_bridge(
-                cls.loop0.sw_if_index, bd_id=cls.bd_id, bvi=1)
+                cls.loop0.sw_if_index, bd_id=cls.bd_id,
+                port_type=L2_PORT_TYPE.BVI)
             cls.vapi.sw_interface_set_l2_bridge(
                 cls.pg0.sw_if_index, bd_id=cls.bd_id)
             cls.vapi.sw_interface_set_l2_bridge(
diff --git a/test/test_dvr.py b/test/test_dvr.py
index 9d86758..7a744ba 100644
--- a/test/test_dvr.py
+++ b/test/test_dvr.py
@@ -4,7 +4,7 @@
 from framework import VppTestCase, VppTestRunner
 from vpp_sub_interface import VppDot1QSubint
 from vpp_ip_route import VppIpRoute, VppRoutePath
-from vpp_papi_provider import L2_VTR_OP
+from vpp_papi_provider import L2_VTR_OP, L2_PORT_TYPE
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether, Dot1Q
@@ -88,7 +88,8 @@
         self.vapi.sw_interface_set_l2_bridge(self.pg1.sw_if_index, 1)
         self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg2.sw_if_index, 1)
         self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index, 1)
-        self.vapi.sw_interface_set_l2_bridge(self.loop0.sw_if_index, 1, bvi=1)
+        self.vapi.sw_interface_set_l2_bridge(self.loop0.sw_if_index, 1,
+                                             port_type=L2_PORT_TYPE.BVI)
 
         self.vapi.sw_interface_set_l2_tag_rewrite(sub_if_on_pg2.sw_if_index,
                                                   L2_VTR_OP.L2_POP_1,
@@ -208,7 +209,8 @@
         self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index,
                                              1, enable=0)
         self.vapi.sw_interface_set_l2_bridge(self.loop0.sw_if_index,
-                                             1, bvi=1, enable=0)
+                                             1, port_type=L2_PORT_TYPE.BVI,
+                                             enable=0)
 
         #
         # Do a FIB dump to make sure the paths are correctly reported as DVR
diff --git a/test/test_gbp.py b/test/test_gbp.py
index 0d5dd15..132bd24 100644
--- a/test/test_gbp.py
+++ b/test/test_gbp.py
@@ -9,6 +9,7 @@
 
 from vpp_ip import *
 from vpp_mac import *
+from vpp_papi_provider import L2_PORT_TYPE
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether, ARP
@@ -585,9 +586,11 @@
             # epg[1] shares the same BVI to epg[0]
             if epg != epgs[1] and epg != epgs[4]:
                 # BVI in BD
-                self.vapi.sw_interface_set_l2_bridge(epg.bvi.sw_if_index,
-                                                     epg.bd,
-                                                     bvi=1)
+                self.vapi.sw_interface_set_l2_bridge(
+                    epg.bvi.sw_if_index,
+                    epg.bd,
+                    port_type=L2_PORT_TYPE.BVI)
+
                 # BVI L2 FIB entry
                 self.vapi.l2fib_add_del(self.router_mac,
                                         epg.bd,
diff --git a/test/test_ip4_irb.py b/test/test_ip4_irb.py
index 6aad60a..cf7d89e 100644
--- a/test/test_ip4_irb.py
+++ b/test/test_ip4_irb.py
@@ -32,6 +32,7 @@
 
 from framework import VppTestCase, VppTestRunner
 from util import mactobinary
+from vpp_papi_provider import L2_PORT_TYPE
 
 
 class TestIpIrb(VppTestCase):
@@ -65,7 +66,8 @@
 
         # Create BD with MAC learning enabled and put interfaces to this BD
         cls.vapi.sw_interface_set_l2_bridge(
-            cls.loop0.sw_if_index, bd_id=cls.bd_id, bvi=1)
+            cls.loop0.sw_if_index, bd_id=cls.bd_id,
+            port_type=L2_PORT_TYPE.BVI)
         cls.vapi.sw_interface_set_l2_bridge(
             cls.pg0.sw_if_index, bd_id=cls.bd_id)
         cls.vapi.sw_interface_set_l2_bridge(
diff --git a/test/test_l2_flood.py b/test/test_l2_flood.py
index a8b6b10..50a692e 100644
--- a/test/test_l2_flood.py
+++ b/test/test_l2_flood.py
@@ -5,6 +5,7 @@
 
 from framework import VppTestCase, VppTestRunner
 from vpp_ip_route import VppIpRoute, VppRoutePath
+from vpp_papi_provider import L2_PORT_TYPE, BRIDGE_FLAGS
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether
@@ -58,7 +59,8 @@
         for i in self.pg_interfaces[8:12]:
             self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, 2)
         for i in self.lo_interfaces:
-            self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, 2, bvi=1)
+            self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, 2,
+                                                 port_type=L2_PORT_TYPE.BVI)
 
         p = (Ether(dst="ff:ff:ff:ff:ff:ff",
                    src="00:00:de:ad:be:ef") /
@@ -137,7 +139,112 @@
             self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, enable=0)
         for i in self.lo_interfaces:
             self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, 2,
-                                                 bvi=1, enable=0)
+                                                 port_type=L2_PORT_TYPE.BVI,
+                                                 enable=0)
+
+        self.vapi.bridge_domain_add_del(1, is_add=0)
+
+    def test_uu_fwd(self):
+        """ UU Flood """
+
+        #
+        # Create a single bridge Domain
+        #
+        self.vapi.bridge_domain_add_del(1, uu_flood=1)
+
+        #
+        # add each interface to the BD. 3 interfaces per split horizon group
+        #
+        for i in self.pg_interfaces[0:4]:
+            self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, 0)
+
+        #
+        # an unknown unicast packet
+        #
+        p_uu = (Ether(dst="00:00:00:c1:5c:00",
+                      src="00:00:de:ad:be:ef") /
+                IP(src="10.10.10.10", dst="1.1.1.1") /
+                UDP(sport=1234, dport=1234) /
+                Raw('\xa5' * 100))
+
+        #
+        # input on pg0, expected copies on pg1->4
+        #
+        self.pg0.add_stream(p_uu*65)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        for i in self.pg_interfaces[1:4]:
+            rx0 = i.get_capture(65, timeout=1)
+
+        #
+        # use pg8 as the uu-fwd interface
+        #
+        self.vapi.sw_interface_set_l2_bridge(self.pg8.sw_if_index, 1, 0,
+                                             port_type=L2_PORT_TYPE.UU_FWD)
+
+        #
+        # expect the UU packet on the uu-fwd interface and not be flooded
+        #
+        self.pg0.add_stream(p_uu*65)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        rx0 = self.pg8.get_capture(65, timeout=1)
+
+        for i in self.pg_interfaces[0:4]:
+            i.assert_nothing_captured(remark="UU not flooded")
+
+        #
+        # remove the uu-fwd interface and expect UU to be flooded again
+        #
+        self.vapi.sw_interface_set_l2_bridge(self.pg8.sw_if_index, 1, 0,
+                                             port_type=L2_PORT_TYPE.UU_FWD,
+                                             enable=0)
+
+        self.pg0.add_stream(p_uu*65)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        for i in self.pg_interfaces[1:4]:
+            rx0 = i.get_capture(65, timeout=1)
+
+        #
+        # change the BD config to not support UU-flood
+        #
+        self.vapi.bridge_flags(1, 0, BRIDGE_FLAGS.UU_FLOOD)
+
+        self.send_and_assert_no_replies(self.pg0, p_uu)
+
+        #
+        # re-add the uu-fwd interface
+        #
+        self.vapi.sw_interface_set_l2_bridge(self.pg8.sw_if_index, 1, 0,
+                                             port_type=L2_PORT_TYPE.UU_FWD)
+        self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+
+        self.pg0.add_stream(p_uu*65)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        rx0 = self.pg8.get_capture(65, timeout=1)
+
+        for i in self.pg_interfaces[0:4]:
+            i.assert_nothing_captured(remark="UU not flooded")
+
+        #
+        # remove the uu-fwd interface
+        #
+        self.vapi.sw_interface_set_l2_bridge(self.pg8.sw_if_index, 1, 0,
+                                             port_type=L2_PORT_TYPE.UU_FWD,
+                                             enable=0)
+        self.send_and_assert_no_replies(self.pg0, p_uu)
+
+        #
+        # cleanup
+        #
+        for i in self.pg_interfaces[:4]:
+            self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, enable=0)
 
         self.vapi.bridge_domain_add_del(1, is_add=0)
 
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 0c98f7a..ee45a5f 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -43,6 +43,21 @@
     IP = 3
 
 
+class L2_PORT_TYPE:
+    NORMAL = 0
+    BVI = 1
+    UU_FWD = 2
+
+
+class BRIDGE_FLAGS:
+    NONE = 0
+    LEARN = 1
+    FWD = 2
+    FLOOD = 4
+    UU_FLOOD = 8
+    ARP_TERM = 16
+
+
 class UnexpectedApiReturnValueError(Exception):
     """ exception raised when the API return value is unexpected """
     pass
@@ -627,7 +642,8 @@
         return self.api(self.papi.l2fib_flush_all, {})
 
     def sw_interface_set_l2_bridge(self, sw_if_index, bd_id,
-                                   shg=0, bvi=0, enable=1):
+                                   shg=0, port_type=L2_PORT_TYPE.NORMAL,
+                                   enable=1):
         """Add/remove interface to/from bridge domain.
 
         :param int sw_if_index: Software interface index of the interface.
@@ -641,7 +657,7 @@
                         {'rx_sw_if_index': sw_if_index,
                          'bd_id': bd_id,
                          'shg': shg,
-                         'bvi': bvi,
+                         'port_type': port_type,
                          'enable': enable})
 
     def bridge_flags(self, bd_id, is_set, feature_bitmap):
@@ -650,7 +666,7 @@
 
         :param int bd_id: Bridge domain ID.
         :param int is_set: Set to 1 to enable, set to 0 to disable the feature.
-        :param int feature_bitmap: Bitmap value of the feature to be set:
+        :param int flags: Bitmap value of the feature to be set:
             - learn (1 << 0),
             - forward (1 << 1),
             - flood (1 << 2),
@@ -660,7 +676,7 @@
         return self.api(self.papi.bridge_flags,
                         {'bd_id': bd_id,
                          'is_set': is_set,
-                         'feature_bitmap': feature_bitmap})
+                         'flags': feature_bitmap})
 
     def bridge_domain_dump(self, bd_id=0):
         """