#!/usr/bin/env python3 import unittest from framework import VppTestCase from asfframework import VppTestRunner from vpp_ip_route import VppIpRoute, VppRoutePath from vpp_l2 import L2_PORT_TYPE, BRIDGE_FLAGS from scapy.packet import Raw from scapy.layers.l2 import Ether from scapy.layers.inet import IP, UDP NUM_PKTS = 67 class TestL2Flood(VppTestCase): """L2-flood""" @classmethod def setUpClass(cls): super(TestL2Flood, cls).setUpClass() @classmethod def tearDownClass(cls): super(TestL2Flood, cls).tearDownClass() def setUp(self): super(TestL2Flood, self).setUp() # 12 l2 interface and one l3 self.create_pg_interfaces(range(13)) self.create_bvi_interfaces(1) for i in self.pg_interfaces: i.admin_up() for i in self.bvi_interfaces: i.admin_up() self.pg12.config_ip4() self.pg12.resolve_arp() self.bvi0.config_ip4() def tearDown(self): self.pg12.unconfig_ip4() self.bvi0.unconfig_ip4() for i in self.pg_interfaces: i.admin_down() for i in self.bvi_interfaces: i.admin_down() super(TestL2Flood, self).tearDown() def test_flood(self): """L2 Flood Tests""" # # Create a single bridge Domain # self.vapi.bridge_domain_add_del_v2( bd_id=1, is_add=1, flood=1, uu_flood=1, forward=1, learn=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( rx_sw_if_index=i.sw_if_index, bd_id=1, shg=0 ) for i in self.pg_interfaces[4:8]: self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=i.sw_if_index, bd_id=1, shg=1 ) for i in self.pg_interfaces[8:12]: self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=i.sw_if_index, bd_id=1, shg=2 ) for i in self.bvi_interfaces: self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=i.sw_if_index, bd_id=1, shg=2, port_type=L2_PORT_TYPE.BVI ) p = ( Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:de:ad:be:ef") / IP(src="10.10.10.10", dst="1.1.1.1") / UDP(sport=1234, dport=1234) / Raw(b"\xa5" * 100) ) # # input on pg0 expect copies on pg1->11 # this is in SHG=0 so its flooded to all, expect the pg0 since that's # the ingress link # self.pg0.add_stream(p * NUM_PKTS) self.pg_enable_capture(self.pg_interfaces) self.pg_start() for i in self.pg_interfaces[1:12]: rx0 = i.get_capture(NUM_PKTS, timeout=1) # # input on pg4 (SHG=1) expect copies on pg0->3 (SHG=0) # and pg8->11 (SHG=2) # self.pg4.add_stream(p * NUM_PKTS) self.pg_enable_capture(self.pg_interfaces) self.pg_start() for i in self.pg_interfaces[:4]: rx0 = i.get_capture(NUM_PKTS, timeout=1) for i in self.pg_interfaces[8:12]: rx0 = i.get_capture(NUM_PKTS, timeout=1) for i in self.pg_interfaces[4:8]: i.assert_nothing_captured(remark="Different SH group") # # An IP route so the packet that hits the BVI is sent out of pg12 # ip_route = VppIpRoute( self, "1.1.1.1", 32, [VppRoutePath(self.pg12.remote_ip4, self.pg12.sw_if_index)], ) ip_route.add_vpp_config() self.logger.info(self.vapi.cli("sh bridge 1 detail")) # # input on pg0 expect copies on pg1->12 # this is in SHG=0 so its flooded to all, expect the pg0 since that's # the ingress link # self.pg0.add_stream(p * NUM_PKTS) self.pg_enable_capture(self.pg_interfaces) self.pg_start() for i in self.pg_interfaces[1:]: rx0 = i.get_capture(NUM_PKTS, timeout=1) # # input on pg4 (SHG=1) expect copies on pg0->3 (SHG=0) # and pg8->12 (SHG=2) # self.pg4.add_stream(p * NUM_PKTS) self.pg_enable_capture(self.pg_interfaces) self.pg_start() for i in self.pg_interfaces[:4]: rx0 = i.get_capture(NUM_PKTS, timeout=1) for i in self.pg_interfaces[8:13]: rx0 = i.get_capture(NUM_PKTS, timeout=1) for i in self.pg_interfaces[4:8]: i.assert_nothing_captured(remark="Different SH group") # # cleanup # for i in self.pg_interfaces[:12]: self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=i.sw_if_index, bd_id=1, enable=0 ) for i in self.bvi_interfaces: self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=i.sw_if_index, bd_id=1, shg=2, port_type=L2_PORT_TYPE.BVI, enable=0, ) self.vapi.bridge_domain_add_del_v2(bd_id=1, is_add=0) def test_flood_one(self): """L2 no-Flood Test""" # # Create a single bridge Domain # self.vapi.bridge_domain_add_del_v2( bd_id=1, is_add=1, flood=1, uu_flood=1, forward=1, learn=1 ) # # add 2 interfaces to the BD. this means a flood goes to only # one member # for i in self.pg_interfaces[:2]: self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=i.sw_if_index, bd_id=1, shg=0 ) p = ( Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:de:ad:be:ef") / IP(src="10.10.10.10", dst="1.1.1.1") / UDP(sport=1234, dport=1234) / Raw(b"\xa5" * 100) ) # # input on pg0 expect copies on pg1 # self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg1) # # cleanup # for i in self.pg_interfaces[:2]: self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=i.sw_if_index, bd_id=1, enable=0 ) self.vapi.bridge_domain_add_del_v2(bd_id=1, is_add=0) def test_uu_fwd(self): """UU Flood""" # # Create a single bridge Domain # self.vapi.bridge_domain_add_del_v2( bd_id=1, is_add=1, uu_flood=1, flood=1, forward=1, learn=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( rx_sw_if_index=i.sw_if_index, bd_id=1, shg=0 ) # # an unknown unicast and broadcast packets # 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(b"\xa5" * 100) ) p_bm = ( Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:de:ad:be:ef") / IP(src="10.10.10.10", dst="1.1.1.1") / UDP(sport=1234, dport=1234) / Raw(b"\xa5" * 100) ) # # input on pg0, expected copies on pg1->4 # self.pg0.add_stream(p_uu * NUM_PKTS) self.pg_enable_capture(self.pg_interfaces) self.pg_start() for i in self.pg_interfaces[1:4]: rx0 = i.get_capture(NUM_PKTS, timeout=1) self.pg0.add_stream(p_bm * NUM_PKTS) self.pg_enable_capture(self.pg_interfaces) self.pg_start() for i in self.pg_interfaces[1:4]: rx0 = i.get_capture(NUM_PKTS, timeout=1) # # use pg8 as the uu-fwd interface # self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=self.pg8.sw_if_index, bd_id=1, shg=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 * NUM_PKTS) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx0 = self.pg8.get_capture(NUM_PKTS, timeout=1) for i in self.pg_interfaces[0:4]: i.assert_nothing_captured(remark="UU not flooded") self.pg0.add_stream(p_bm * NUM_PKTS) self.pg_enable_capture(self.pg_interfaces) self.pg_start() for i in self.pg_interfaces[1:4]: rx0 = i.get_capture(NUM_PKTS, timeout=1) # # remove the uu-fwd interface and expect UU to be flooded again # self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=self.pg8.sw_if_index, bd_id=1, shg=0, port_type=L2_PORT_TYPE.UU_FWD, enable=0, ) self.pg0.add_stream(p_uu * NUM_PKTS) self.pg_enable_capture(self.pg_interfaces) self.pg_start() for i in self.pg_interfaces[1:4]: rx0 = i.get_capture(NUM_PKTS, timeout=1) # # change the BD config to not support UU-flood # self.vapi.bridge_flags(bd_id=1, is_set=0, flags=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( rx_sw_if_index=self.pg8.sw_if_index, bd_id=1, shg=0, port_type=L2_PORT_TYPE.UU_FWD, ) self.logger.info(self.vapi.cli("sh bridge 1 detail")) self.pg0.add_stream(p_uu * NUM_PKTS) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx0 = self.pg8.get_capture(NUM_PKTS, 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( rx_sw_if_index=self.pg8.sw_if_index, bd_id=1, shg=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( rx_sw_if_index=i.sw_if_index, bd_id=1, enable=0 ) self.vapi.bridge_domain_add_del_v2(bd_id=1, is_add=0) if __name__ == "__main__": unittest.main(testRunner=VppTestRunner)