diff options
Diffstat (limited to 'test/test_l2_fib.py')
-rw-r--r-- | test/test_l2_fib.py | 542 |
1 files changed, 542 insertions, 0 deletions
diff --git a/test/test_l2_fib.py b/test/test_l2_fib.py new file mode 100644 index 00000000..52bf9c86 --- /dev/null +++ b/test/test_l2_fib.py @@ -0,0 +1,542 @@ +#!/usr/bin/env python +"""L2 FIB Test Case HLD: + +**config 1** + - add 4 pg-l2 interfaces + - configure them into l2bd + - configure 100 MAC entries in L2 fib - 25 MACs per interface + - L2 MAC learning and unknown unicast flooding disabled in l2bd + - configure 100 MAC entries in L2 fib - 25 MACs per interface + +**test 1** + - send L2 MAC frames between all 4 pg-l2 interfaces for all of 100 MAC \ + entries in the FIB + +**verify 1** + - all packets received correctly + +**config 2** + - delete 12 MAC entries - 3 MACs per interface + +**test 2a** + - send L2 MAC frames between all 4 pg-l2 interfaces for non-deleted MAC \ + entries + +**verify 2a** + - all packets received correctly + +**test 2b** + - send L2 MAC frames between all 4 pg-l2 interfaces for all of 12 deleted \ + MAC entries + +**verify 2b** + - no packet received on all 4 pg-l2 interfaces + +**config 3** + - configure new 100 MAC entries in L2 fib - 25 MACs per interface + +**test 3** + - send L2 MAC frames between all 4 pg-l2 interfaces for all of 188 MAC \ + entries in the FIB + +**verify 3** + - all packets received correctly + +**config 4** + - delete 160 MAC entries, 40 MACs per interface + +**test 4a** + - send L2 MAC frames between all 4 pg-l2 interfaces for all of 28 \ + non-deleted MAC entries + +**verify 4a** + - all packets received correctly + +**test 4b** + - try send L2 MAC frames between all 4 pg-l2 interfaces for all of 172 \ + deleted MAC entries + +**verify 4b** + - no packet received on all 4 pg-l2 interfaces +""" + +import unittest +import random + +from scapy.packet import Raw +from scapy.layers.l2 import Ether +from scapy.layers.inet import IP, UDP + +from framework import VppTestCase, VppTestRunner +from util import Host, ppp + + +class TestL2fib(VppTestCase): + """ L2 FIB Test Case """ + + @classmethod + def bd_ifs(cls, bd_id): + return range((bd_id - 1) * cls.n_ifs_per_bd, bd_id * cls.n_ifs_per_bd) + + @classmethod + def setUpClass(cls): + """ + Perform standard class setup (defined by class method setUpClass in + class VppTestCase) before running the test case, set test case related + variables and configure VPP. + + :var int bd_id: Bridge domain ID. + """ + super(TestL2fib, cls).setUpClass() + + try: + n_brs = cls.n_brs = range(1, 3) + cls.n_ifs_per_bd = 4 + n_ifs = range(cls.n_ifs_per_bd * len(cls.n_brs)) + # Create 4 pg interfaces + cls.create_pg_interfaces(n_ifs) + + cls.flows = dict() + for bd_id in n_brs: + # Packet flows mapping pg0 -> pg1, pg2, pg3 etc. + ifs = cls.bd_ifs(bd_id) + for j in ifs: + cls.flows[cls.pg_interfaces[j]] = [ + cls.pg_interfaces[x] for x in ifs if x != j] + + # Packet sizes + cls.pg_if_packet_sizes = [64, 512, 1518, 9018] + + for bd_id in n_brs: + # Create BD with MAC learning and unknown unicast flooding + # disabled and put interfaces to this BD + cls.vapi.bridge_domain_add_del( + bd_id=bd_id, uu_flood=0, learn=0) + ifs = [cls.pg_interfaces[i] for i in cls.bd_ifs(bd_id)] + for pg_if in ifs: + cls.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index, + bd_id=bd_id) + + # Set up all interfaces + for i in cls.pg_interfaces: + i.admin_up() + + # Mapping between packet-generator index and lists of test hosts + cls.hosts = dict() + cls.learned_hosts = dict() + cls.fib_hosts = dict() + cls.deleted_hosts = dict() + for pg_if in cls.pg_interfaces: + swif = pg_if.sw_if_index + cls.hosts[swif] = [] + cls.learned_hosts[swif] = [] + cls.fib_hosts[swif] = [] + cls.deleted_hosts[swif] = [] + + except Exception: + super(TestL2fib, cls).tearDownClass() + raise + + def setUp(self): + super(TestL2fib, self).setUp() + self.reset_packet_infos() + + def tearDown(self): + """ + Show various debug prints after each test. + """ + super(TestL2fib, self).tearDown() + if not self.vpp_dead: + self.logger.info(self.vapi.ppcli("show l2fib verbose")) + for bd_id in self.n_brs: + self.logger.info(self.vapi.ppcli("show bridge-domain %s detail" + % bd_id)) + + def create_hosts(self, n_hosts_per_if, subnet): + """ + Create required number of host MAC addresses and distribute them among + interfaces. Create host IPv4 address for every host MAC address. + + :param int n_hosts_per_if: Number of per interface hosts to + create MAC/IPv4 addresses for. + """ + + for pg_if in self.pg_interfaces: + swif = pg_if.sw_if_index + + def mac(j): return "00:00:%02x:ff:%02x:%02x" % (subnet, swif, j) + + def ip(j): return "172.%02u.1%02x.%u" % (subnet, swif, j) + + def h(j): return Host(mac(j), ip(j)) + self.hosts[swif] = [h(j) for j in range(n_hosts_per_if)] + + def learn_hosts(self, bd_id, n_hosts_per_if): + """ + Create L2 MAC packet stream with host MAC addresses per interface to + let the bridge domain learn these MAC addresses. + + :param int bd_id: BD to teach + :param int n_hosts_per_if: number of hosts + """ + self.vapi.bridge_flags(bd_id, 1, 1) + ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)] + for pg_if in ifs: + swif = pg_if.sw_if_index + hosts = self.hosts[swif] + lhosts = self.learned_hosts[swif] + packets = [] + for j in range(n_hosts_per_if): + host = hosts.pop() + lhosts.append(host) + packet = (Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)) + packets.append(packet) + pg_if.add_stream(packets) + self.logger.info("Sending broadcast eth frames for MAC learning") + self.pg_start() + + def config_l2_fib_entries(self, bd_id, n_hosts_per_if): + """ + Config required number of L2 FIB entries. + + :param int bd_id: BD's id + :param int count: Number of L2 FIB entries to be created. + :param int start: Starting index of the host list. (Default value = 0) + """ + ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)] + for pg_if in ifs: + swif = pg_if.sw_if_index + hosts = self.hosts[swif] + fhosts = self.fib_hosts[swif] + for j in range(n_hosts_per_if): + host = hosts.pop() + self.vapi.l2fib_add_del( + host.mac, bd_id, swif, static_mac=1) + fhosts.append(host) + # del hosts[0] + self.logger.info("Configure %d L2 FIB entries .." % + len(self.pg_interfaces) * n_hosts_per_if) + self.logger.info(self.vapi.ppcli("show l2fib")) + + def delete_l2_fib_entry(self, bd_id, n_hosts_per_if): + """ + Delete required number of L2 FIB entries. + + :param int count: Number of L2 FIB entries to be created. + """ + ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)] + for pg_if in ifs: + swif = pg_if.sw_if_index + hosts = self.fib_hosts[swif] + dhosts = self.deleted_hosts[swif] + for j in range(n_hosts_per_if): + host = hosts.pop() + self.vapi.l2fib_add_del( + host.mac, bd_id, swif, is_add=0) + dhosts.append(host) + self.logger.info(self.vapi.ppcli("show l2fib")) + + def flush_int(self, swif): + """ + Flush swif L2 FIB entries. + + :param int swif: sw if index. + """ + flushed = dict() + self.vapi.l2fib_flush_int(swif) + self.deleted_hosts[swif] = self.learned_hosts[swif] + \ + self.deleted_hosts[swif] + flushed[swif] = self.learned_hosts[swif] + self.learned_hosts[swif] = [] + self.logger.info(self.vapi.ppcli("show l2fib")) + return flushed + + def flush_bd(self, bd_id): + """ + Flush bd_id L2 FIB entries. + + :param int bd_id: Bridge Domain id. + """ + self.vapi.l2fib_flush_bd(bd_id) + flushed = dict() + ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)] + for pg_if in ifs: + swif = pg_if.sw_if_index + self.deleted_hosts[swif] = self.learned_hosts[swif] + \ + self.deleted_hosts[swif] + flushed[swif] = self.learned_hosts[swif] + self.learned_hosts[swif] = [] + self.logger.info(self.vapi.ppcli("show l2fib")) + return flushed + + def flush_all(self): + """ + Flush All L2 FIB entries. + """ + self.vapi.l2fib_flush_all() + flushed = dict() + for pg_if in self.pg_interfaces: + swif = pg_if.sw_if_index + self.deleted_hosts[swif] = self.learned_hosts[swif] + \ + self.deleted_hosts[swif] + flushed[swif] = self.learned_hosts[swif] + self.learned_hosts[swif] = [] + self.logger.info(self.vapi.ppcli("show l2fib")) + return flushed + + def create_stream(self, src_if, packet_sizes, if_src_hosts=None, + if_dst_hosts=None): + """ + Create input packet stream for defined interface using hosts or + deleted_hosts list. + + :param object src_if: Interface to create packet stream for. + :param list packet_sizes: List of required packet sizes. + :param boolean deleted: Set to True if deleted_hosts list required. + :return: Stream of packets. + """ + if not if_src_hosts: + if_src_hosts = self.fib_hosts + if not if_dst_hosts: + if_dst_hosts = self.fib_hosts + src_hosts = if_src_hosts[src_if.sw_if_index] + if not src_hosts: + return [] + pkts = [] + for dst_if in self.flows[src_if]: + dst_swif = dst_if.sw_if_index + if dst_swif not in if_dst_hosts: + continue + dst_hosts = if_dst_hosts[dst_swif] + for i in range(0, len(dst_hosts)): + dst_host = dst_hosts[i] + src_host = random.choice(src_hosts) + pkt_info = self.create_packet_info(src_if, dst_if) + payload = self.info_to_payload(pkt_info) + p = (Ether(dst=dst_host.mac, src=src_host.mac) / + IP(src=src_host.ip4, dst=dst_host.ip4) / + UDP(sport=1234, dport=1234) / + Raw(payload)) + pkt_info.data = p.copy() + size = random.choice(packet_sizes) + self.extend_packet(p, size) + pkts.append(p) + return pkts + + def verify_capture(self, pg_if, capture): + """ + Verify captured input packet stream for defined interface. + + :param object pg_if: Interface to verify captured packet stream for. + :param list capture: Captured packet stream. + """ + last_info = dict() + for i in self.pg_interfaces: + last_info[i.sw_if_index] = None + dst_sw_if_index = pg_if.sw_if_index + for packet in capture: + payload_info = self.payload_to_info(str(packet[Raw])) + try: + ip = packet[IP] + udp = packet[UDP] + packet_index = payload_info.index + self.assertEqual(payload_info.dst, dst_sw_if_index) + self.logger.debug("Got packet on port %s: src=%u (id=%u)" % + (pg_if.name, payload_info.src, packet_index)) + next_info = self.get_next_packet_info_for_interface2( + payload_info.src, dst_sw_if_index, + last_info[payload_info.src]) + last_info[payload_info.src] = next_info + self.assertTrue(next_info is not None) + self.assertEqual(packet_index, next_info.index) + saved_packet = next_info.data + # Check standard fields + self.assertEqual(ip.src, saved_packet[IP].src) + self.assertEqual(ip.dst, saved_packet[IP].dst) + self.assertEqual(udp.sport, saved_packet[UDP].sport) + self.assertEqual(udp.dport, saved_packet[UDP].dport) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + for i in self.pg_interfaces: + remaining_packet = self.get_next_packet_info_for_interface2( + i, dst_sw_if_index, last_info[i.sw_if_index]) + self.assertTrue( + remaining_packet is None, + "Port %u: Packet expected from source %u didn't arrive" % + (dst_sw_if_index, i.sw_if_index)) + + def run_verify_test(self, bd_id, dst_hosts=None): + # Test + # Create incoming packet streams for packet-generator interfaces + if not dst_hosts: + dst_hosts = self.fib_hosts + self.reset_packet_infos() + ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)] + for i in ifs: + pkts = self.create_stream( + i, self.pg_if_packet_sizes, if_dst_hosts=dst_hosts) + i.add_stream(pkts) + + self.vapi.bridge_flags(bd_id, 0, 1) + # Enable packet capture and start packet sending + self.pg_enable_capture(ifs) + self.pg_start() + + # Verify + # Verify outgoing packet streams per packet-generator interface + for i in ifs: + if not dst_hosts[i.sw_if_index]: + continue + capture = i.get_capture() + self.logger.info("Verifying capture on interface %s" % i.name) + self.verify_capture(i, capture) + + def run_verify_negat_test(self, bd_id, dst_hosts=None): + # Test + # Create incoming packet streams for packet-generator interfaces for + # deleted MAC addresses + if not dst_hosts: + dst_hosts = self.deleted_hosts + self.reset_packet_infos() + ifs = [self.pg_interfaces[i] for i in self.bd_ifs(bd_id)] + for i in ifs: + pkts = self.create_stream( + i, self.pg_if_packet_sizes, if_dst_hosts=dst_hosts) + if pkts: + i.add_stream(pkts) + + self.vapi.bridge_flags(bd_id, 0, 1) + # Enable packet capture and start packet sending + self.pg_enable_capture(ifs) + self.pg_start() + + # Verify + # Verify outgoing packet streams per packet-generator interface + timeout = 1 + for i in ifs: + i.get_capture(0, timeout=timeout) + i.assert_nothing_captured(remark="outgoing interface") + timeout = 0.1 + + def test_l2_fib_01(self): + """ L2 FIB test 1 - program 100 MAC addresses + """ + # Config 1 + # Create test host entries + self.create_hosts(100, subnet=17) + + # Add first 100 MAC entries to L2 FIB + self.config_l2_fib_entries(bd_id=1, n_hosts_per_if=100) + + # Test 1 + self.run_verify_test(bd_id=1) + + def test_l2_fib_02(self): + """ L2 FIB test 2 - delete 12 MAC entries + """ + # Config 2 + # Delete 12 MAC entries per interface from L2 FIB + self.delete_l2_fib_entry(bd_id=1, n_hosts_per_if=12) + + # Test 2a + self.run_verify_test(bd_id=1) + + # Verify 2a + self.run_verify_negat_test(bd_id=1) + + def test_l2_fib_03(self): + """ L2 FIB test 3 - program new 100 MAC addresses + """ + # Config 3 + # Create new test host entries + self.create_hosts(100, subnet=22) + + # Add new 100 MAC entries to L2 FIB + self.config_l2_fib_entries(bd_id=1, n_hosts_per_if=100) + + # Test 3 + self.run_verify_test(bd_id=1) + + def test_l2_fib_04(self): + """ L2 FIB test 4 - delete 160 MAC entries + """ + # Config 4 + # Delete 160 MAC entries per interface from L2 FIB + self.delete_l2_fib_entry(bd_id=1, n_hosts_per_if=160) + + # Test 4a + self.run_verify_negat_test(bd_id=1) + + def test_l2_fib_05(self): + """ L2 FIB test 5 - Program 10 new MAC entries, learn 10 + """ + self.create_hosts(20, subnet=35) + + self.learn_hosts(bd_id=1, n_hosts_per_if=10) + self.learn_hosts(bd_id=2, n_hosts_per_if=10) + self.config_l2_fib_entries(bd_id=1, n_hosts_per_if=10) + self.config_l2_fib_entries(bd_id=2, n_hosts_per_if=10) + self.run_verify_test(bd_id=1, dst_hosts=self.learned_hosts) + self.run_verify_test(bd_id=2, dst_hosts=self.learned_hosts) + + def test_l2_fib_06(self): + """ L2 FIB test 6 - flush first interface + """ + self.create_hosts(20, subnet=36) + + self.learn_hosts(bd_id=1, n_hosts_per_if=10) + self.learn_hosts(bd_id=2, n_hosts_per_if=10) + self.config_l2_fib_entries(bd_id=1, n_hosts_per_if=10) + self.config_l2_fib_entries(bd_id=2, n_hosts_per_if=10) + flushed = self.flush_int(self.pg_interfaces[0].sw_if_index) + self.run_verify_test(bd_id=1, dst_hosts=self.learned_hosts) + self.run_verify_negat_test(bd_id=1, dst_hosts=flushed) + + def test_l2_fib_07(self): + """ L2 FIB test 7 - flush bd_id + """ + self.create_hosts(20, subnet=37) + + self.learn_hosts(bd_id=1, n_hosts_per_if=10) + self.learn_hosts(bd_id=2, n_hosts_per_if=10) + self.config_l2_fib_entries(bd_id=1, n_hosts_per_if=10) + self.config_l2_fib_entries(bd_id=2, n_hosts_per_if=10) + flushed = self.flush_bd(bd_id=1) + self.run_verify_negat_test(bd_id=1, dst_hosts=flushed) + self.run_verify_test(bd_id=2, dst_hosts=self.learned_hosts) + + def test_l2_fib_08(self): + """ L2 FIB test 8 - flush all + """ + self.create_hosts(20, subnet=38) + + self.learn_hosts(bd_id=1, n_hosts_per_if=10) + self.learn_hosts(bd_id=2, n_hosts_per_if=10) + self.config_l2_fib_entries(bd_id=1, n_hosts_per_if=10) + self.config_l2_fib_entries(bd_id=2, n_hosts_per_if=10) + flushed = self.flush_all() + self.run_verify_negat_test(bd_id=1, dst_hosts=flushed) + self.run_verify_negat_test(bd_id=2, dst_hosts=flushed) + + def test_l2_fib_09(self): + """ L2 FIB test 9 - mac learning events + """ + self.create_hosts(10, subnet=39) + + self.vapi.want_macs_learn_events() + self.learn_hosts(bd_id=1, n_hosts_per_if=10) + + self.sleep(1) + self.logger.info(self.vapi.ppcli("show l2fib")) + evs = self.vapi.collect_events() + learned_macs = { + e.mac[i].mac_addr for e in evs for i in range(e.n_macs)} + macs = {h.bin_mac for swif_hs in self.learned_hosts.itervalues() + for h in swif_hs} + self.vapi.want_macs_learn_events(enable_disable=0) + self.assertEqual(len(learned_macs ^ macs), 0) + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) |