1ead1e536SRenato Botelho do Couto#!/usr/bin/env python3
29cd2d7a5SSteven
39cd2d7a5SStevenimport socket
49cd2d7a5SStevenimport unittest
59cd2d7a5SSteven
69cd2d7a5SStevenfrom framework import VppTestCase, VppTestRunner
79cd2d7a5SStevenfrom scapy.packet import Raw
89cd2d7a5SStevenfrom scapy.layers.l2 import Ether
99cd2d7a5SStevenfrom scapy.layers.inet import IP, UDP
109cd2d7a5SStevenfrom vpp_bond_interface import VppBondInterface
118006c6aaSOle Troanfrom vpp_papi import MACAddress
129cd2d7a5SSteven
139cd2d7a5SSteven
149cd2d7a5SStevenclass TestBondInterface(VppTestCase):
159cd2d7a5SSteven    """Bond Test Case
169cd2d7a5SSteven
179cd2d7a5SSteven    """
189cd2d7a5SSteven
199cd2d7a5SSteven    @classmethod
209cd2d7a5SSteven    def setUpClass(cls):
219cd2d7a5SSteven        super(TestBondInterface, cls).setUpClass()
229cd2d7a5SSteven        # Test variables
239cd2d7a5SSteven        cls.pkts_per_burst = 257    # Number of packets per burst
249cd2d7a5SSteven        # create 3 pg interfaces
259cd2d7a5SSteven        cls.create_pg_interfaces(range(4))
269cd2d7a5SSteven
279cd2d7a5SSteven        # packet sizes
289cd2d7a5SSteven        cls.pg_if_packet_sizes = [64, 512, 1518]  # , 9018]
299cd2d7a5SSteven
309cd2d7a5SSteven        # setup all interfaces
319cd2d7a5SSteven        for i in cls.pg_interfaces:
329cd2d7a5SSteven            i.admin_up()
339cd2d7a5SSteven
347f9b7f9fSPaul Vinciguerra    @classmethod
357f9b7f9fSPaul Vinciguerra    def tearDownClass(cls):
367f9b7f9fSPaul Vinciguerra        super(TestBondInterface, cls).tearDownClass()
377f9b7f9fSPaul Vinciguerra
389cd2d7a5SSteven    def setUp(self):
399cd2d7a5SSteven        super(TestBondInterface, self).setUp()
409cd2d7a5SSteven
419cd2d7a5SSteven    def tearDown(self):
429cd2d7a5SSteven        super(TestBondInterface, self).tearDown()
4390cf21b5SPaul Vinciguerra
4490cf21b5SPaul Vinciguerra    def show_commands_at_teardown(self):
4590cf21b5SPaul Vinciguerra        self.logger.info(self.vapi.ppcli("show interface"))
469cd2d7a5SSteven
479cd2d7a5SSteven    def test_bond_traffic(self):
489cd2d7a5SSteven        """ Bond traffic test """
499cd2d7a5SSteven
509cd2d7a5SSteven        # topology
519cd2d7a5SSteven        #
529cd2d7a5SSteven        # RX->              TX->
539cd2d7a5SSteven        #
549cd2d7a5SSteven        # pg2 ------+        +------pg0 (slave)
559cd2d7a5SSteven        #           |        |
569cd2d7a5SSteven        #          BondEthernet0 (10.10.10.1)
579cd2d7a5SSteven        #           |        |
589cd2d7a5SSteven        # pg3 ------+        +------pg1 (slave)
599cd2d7a5SSteven        #
609cd2d7a5SSteven
619cd2d7a5SSteven        # create interface (BondEthernet0)
629cd2d7a5SSteven        #        self.logger.info("create bond")
639cd2d7a5SSteven        bond0_mac = "02:fe:38:30:59:3c"
648006c6aaSOle Troan        mac = MACAddress(bond0_mac).packed
659cd2d7a5SSteven        bond0 = VppBondInterface(self,
669cd2d7a5SSteven                                 mode=3,
679cd2d7a5SSteven                                 lb=1,
68751e3f38SZhiyong Yang                                 numa_only=0,
699cd2d7a5SSteven                                 use_custom_mac=1,
709cd2d7a5SSteven                                 mac_address=mac)
719cd2d7a5SSteven        bond0.add_vpp_config()
729cd2d7a5SSteven        bond0.admin_up()
73053204abSJakub Grajciar        self.vapi.sw_interface_add_del_address(
74053204abSJakub Grajciar            sw_if_index=bond0.sw_if_index,
75efd7bc2bSNeale Ranns            prefix="10.10.10.1/24")
769cd2d7a5SSteven
779cd2d7a5SSteven        self.pg2.config_ip4()
789cd2d7a5SSteven        self.pg2.resolve_arp()
799cd2d7a5SSteven        self.pg3.config_ip4()
809cd2d7a5SSteven        self.pg3.resolve_arp()
819cd2d7a5SSteven
829cd2d7a5SSteven        self.logger.info(self.vapi.cli("show interface"))
839cd2d7a5SSteven        self.logger.info(self.vapi.cli("show interface address"))
84cbe25aabSNeale Ranns        self.logger.info(self.vapi.cli("show ip neighbors"))
859cd2d7a5SSteven
869cd2d7a5SSteven        # enslave pg0 and pg1 to BondEthernet0
879cd2d7a5SSteven        self.logger.info("bond enslave interface pg0 to BondEthernet0")
88a1876b84SSteven Luong        bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index)
899cd2d7a5SSteven        self.logger.info("bond enslave interface pg1 to BondEthernet0")
90a1876b84SSteven Luong        bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index)
919cd2d7a5SSteven
929cd2d7a5SSteven        # verify both slaves in BondEthernet0
939cd2d7a5SSteven        if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
949cd2d7a5SSteven        self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
959cd2d7a5SSteven        self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
969cd2d7a5SSteven
979cd2d7a5SSteven        # generate a packet from pg2 -> BondEthernet0 -> pg1
989cd2d7a5SSteven        # BondEthernet0 TX hashes this packet to pg1
999cd2d7a5SSteven        p2 = (Ether(src=bond0_mac, dst=self.pg2.local_mac) /
1009cd2d7a5SSteven              IP(src=self.pg2.local_ip4, dst="10.10.10.12") /
1019cd2d7a5SSteven              UDP(sport=1235, dport=1235) /
102f6dee6c8SOle Troan              Raw(b'\xa5' * 100))
1039cd2d7a5SSteven        self.pg2.add_stream(p2)
1049cd2d7a5SSteven
1059cd2d7a5SSteven        # generate a packet from pg3 -> BondEthernet0 -> pg0
1069cd2d7a5SSteven        # BondEthernet0 TX hashes this packet to pg0
1079cd2d7a5SSteven        # notice the ip address and ports are different than p2 packet
1089cd2d7a5SSteven        p3 = (Ether(src=bond0_mac, dst=self.pg3.local_mac) /
1099cd2d7a5SSteven              IP(src=self.pg3.local_ip4, dst="10.10.10.11") /
1109cd2d7a5SSteven              UDP(sport=1234, dport=1234) /
111f6dee6c8SOle Troan              Raw(b'\xa5' * 100))
1129cd2d7a5SSteven        self.pg3.add_stream(p3)
1139cd2d7a5SSteven
1149cd2d7a5SSteven        self.pg_enable_capture(self.pg_interfaces)
1159cd2d7a5SSteven
1169cd2d7a5SSteven        # set up the static arp entries pointing to the BondEthernet0 interface
1179cd2d7a5SSteven        # so that it does not try to resolve the ip address
1189cd2d7a5SSteven        self.logger.info(self.vapi.cli(
119cbe25aabSNeale Ranns            "set ip neighbor static BondEthernet0 10.10.10.12 abcd.abcd.0002"))
1209cd2d7a5SSteven        self.logger.info(self.vapi.cli(
121cbe25aabSNeale Ranns            "set ip neighbor static BondEthernet0 10.10.10.11 abcd.abcd.0004"))
1229cd2d7a5SSteven
1239cd2d7a5SSteven        # clear the interface counters
1249cd2d7a5SSteven        self.logger.info(self.vapi.cli("clear interfaces"))
1259cd2d7a5SSteven
1269cd2d7a5SSteven        self.pg_start()
1279cd2d7a5SSteven
1289cd2d7a5SSteven        self.logger.info("check the interface counters")
1299cd2d7a5SSteven
1309cd2d7a5SSteven        # verify counters
1319cd2d7a5SSteven
1329cd2d7a5SSteven        # BondEthernet0 tx bytes = 284
1339cd2d7a5SSteven        intfs = self.vapi.cli("show interface BondEthernet0").split("\n")
1349cd2d7a5SSteven        found = 0
1359cd2d7a5SSteven        for intf in intfs:
1369cd2d7a5SSteven            if "tx bytes" in intf and "284" in intf:
1379cd2d7a5SSteven                found = 1
1389cd2d7a5SSteven        self.assertEqual(found, 1)
1399cd2d7a5SSteven
1400d88301aSSteven        # BondEthernet0 tx bytes = 284
1410d88301aSSteven        intfs = self.vapi.cli("show interface BondEthernet0").split("\n")
1429cd2d7a5SSteven        found = 0
1439cd2d7a5SSteven        for intf in intfs:
1440d88301aSSteven            if "tx bytes" in intf and "284" in intf:
1459cd2d7a5SSteven                found = 1
1469cd2d7a5SSteven        self.assertEqual(found, 1)
1479cd2d7a5SSteven
1489cd2d7a5SSteven        # pg2 rx bytes = 142
1499cd2d7a5SSteven        intfs = self.vapi.cli("show interface pg2").split("\n")
1509cd2d7a5SSteven        found = 0
1519cd2d7a5SSteven        for intf in intfs:
1529cd2d7a5SSteven            if "rx bytes" in intf and "142" in intf:
1539cd2d7a5SSteven                found = 1
1549cd2d7a5SSteven        self.assertEqual(found, 1)
1559cd2d7a5SSteven
1569cd2d7a5SSteven        # pg3 rx bytes = 142
1579cd2d7a5SSteven        intfs = self.vapi.cli("show interface pg3").split("\n")
1589cd2d7a5SSteven        found = 0
1599cd2d7a5SSteven        for intf in intfs:
1609cd2d7a5SSteven            if "rx bytes" in intf and "142" in intf:
1619cd2d7a5SSteven                found = 1
1629cd2d7a5SSteven        self.assertEqual(found, 1)
1639cd2d7a5SSteven
1649cd2d7a5SSteven        bond0.remove_vpp_config()
1659cd2d7a5SSteven
1669cd2d7a5SSteven    def test_bond_enslave(self):
1679cd2d7a5SSteven        """ Bond enslave/detach slave test """
1689cd2d7a5SSteven
16972e31bc2SVladimir Isaev        # create interface (BondEthernet0) and set bond mode to LACP
1709cd2d7a5SSteven        self.logger.info("create bond")
17172e31bc2SVladimir Isaev        bond0 = VppBondInterface(self, mode=5)
1729cd2d7a5SSteven        bond0.add_vpp_config()
1739cd2d7a5SSteven        bond0.admin_up()
1749cd2d7a5SSteven
17572e31bc2SVladimir Isaev        # verify that interfaces can be enslaved and detached two times
17672e31bc2SVladimir Isaev        for i in range(2):
17772e31bc2SVladimir Isaev            # verify pg0 and pg1 not in BondEthernet0
17872e31bc2SVladimir Isaev            if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
17972e31bc2SVladimir Isaev            self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
18072e31bc2SVladimir Isaev            self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
18172e31bc2SVladimir Isaev
18272e31bc2SVladimir Isaev            # enslave pg0 and pg1 to BondEthernet0
18372e31bc2SVladimir Isaev            self.logger.info("bond enslave interface pg0 to BondEthernet0")
18472e31bc2SVladimir Isaev            bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index,
18572e31bc2SVladimir Isaev                                             is_passive=0,
18672e31bc2SVladimir Isaev                                             is_long_timeout=0)
18772e31bc2SVladimir Isaev
18872e31bc2SVladimir Isaev            self.logger.info("bond enslave interface pg1 to BondEthernet0")
18972e31bc2SVladimir Isaev            bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index,
19072e31bc2SVladimir Isaev                                             is_passive=0,
19172e31bc2SVladimir Isaev                                             is_long_timeout=0)
19272e31bc2SVladimir Isaev            # verify both slaves in BondEthernet0
19372e31bc2SVladimir Isaev            if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
19472e31bc2SVladimir Isaev            self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
19572e31bc2SVladimir Isaev            self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
19672e31bc2SVladimir Isaev
19772e31bc2SVladimir Isaev            # detach interface pg0
19872e31bc2SVladimir Isaev            self.logger.info("detach interface pg0")
19972e31bc2SVladimir Isaev            bond0.detach_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index)
20072e31bc2SVladimir Isaev
20172e31bc2SVladimir Isaev            # verify pg0 is not in BondEthernet0, but pg1 is
20272e31bc2SVladimir Isaev            if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
20372e31bc2SVladimir Isaev            self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
20472e31bc2SVladimir Isaev            self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
20572e31bc2SVladimir Isaev
20672e31bc2SVladimir Isaev            # detach interface pg1
20772e31bc2SVladimir Isaev            self.logger.info("detach interface pg1")
20872e31bc2SVladimir Isaev            bond0.detach_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index)
20972e31bc2SVladimir Isaev
21072e31bc2SVladimir Isaev            # verify pg0 and pg1 not in BondEthernet0
21172e31bc2SVladimir Isaev            if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
21272e31bc2SVladimir Isaev            self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
21372e31bc2SVladimir Isaev            self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
2149cd2d7a5SSteven
2159cd2d7a5SSteven        bond0.remove_vpp_config()
2169cd2d7a5SSteven
2179cd2d7a5SSteven    def test_bond(self):
2189cd2d7a5SSteven        """ Bond add/delete interface test """
2199cd2d7a5SSteven        self.logger.info("Bond add interfaces")
2209cd2d7a5SSteven
2219cd2d7a5SSteven        # create interface 1 (BondEthernet0)
2229cd2d7a5SSteven        bond0 = VppBondInterface(self, mode=5)
2239cd2d7a5SSteven        bond0.add_vpp_config()
2249cd2d7a5SSteven        bond0.admin_up()
2259cd2d7a5SSteven
2269cd2d7a5SSteven        # create interface 2 (BondEthernet1)
2279cd2d7a5SSteven        bond1 = VppBondInterface(self, mode=3)
2289cd2d7a5SSteven        bond1.add_vpp_config()
2299cd2d7a5SSteven        bond1.admin_up()
2309cd2d7a5SSteven
2319cd2d7a5SSteven        # verify both interfaces in the show
2329cd2d7a5SSteven        ifs = self.vapi.cli("show interface")
2339a6dafd5SPaul Vinciguerra        self.assertIn('BondEthernet0', ifs)
2349a6dafd5SPaul Vinciguerra        self.assertIn('BondEthernet1', ifs)
2359cd2d7a5SSteven
2369cd2d7a5SSteven        # verify they are in the dump also
2379cd2d7a5SSteven        if_dump = self.vapi.sw_interface_bond_dump()
2389cd2d7a5SSteven        self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
2399cd2d7a5SSteven        self.assertTrue(bond1.is_interface_config_in_dump(if_dump))
2409cd2d7a5SSteven
2419cd2d7a5SSteven        # delete BondEthernet1
2429cd2d7a5SSteven        self.logger.info("Deleting BondEthernet1")
2439cd2d7a5SSteven        bond1.remove_vpp_config()
2449cd2d7a5SSteven
2459cd2d7a5SSteven        self.logger.info("Verifying BondEthernet1 is deleted")
2469cd2d7a5SSteven
2479cd2d7a5SSteven        ifs = self.vapi.cli("show interface")
2489cd2d7a5SSteven        # verify BondEthernet0 still in the show
2499a6dafd5SPaul Vinciguerra        self.assertIn('BondEthernet0', ifs)
2509cd2d7a5SSteven
2519cd2d7a5SSteven        # verify BondEthernet1 not in the show
2529a6dafd5SPaul Vinciguerra        self.assertNotIn('BondEthernet1', ifs)
2539cd2d7a5SSteven
2549cd2d7a5SSteven        # verify BondEthernet1 is not in the dump
2559cd2d7a5SSteven        if_dump = self.vapi.sw_interface_bond_dump()
2569cd2d7a5SSteven        self.assertFalse(bond1.is_interface_config_in_dump(if_dump))
2579cd2d7a5SSteven
2589cd2d7a5SSteven        # verify BondEthernet0 is still in the dump
2599cd2d7a5SSteven        self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
2609cd2d7a5SSteven
2619cd2d7a5SSteven        # delete BondEthernet0
2629cd2d7a5SSteven        self.logger.info("Deleting BondEthernet0")
2639cd2d7a5SSteven        bond0.remove_vpp_config()
2649cd2d7a5SSteven
2659cd2d7a5SSteven        self.logger.info("Verifying BondEthernet0 is deleted")
2669cd2d7a5SSteven
2679cd2d7a5SSteven        # verify BondEthernet0 not in the show
2689cd2d7a5SSteven        ifs = self.vapi.cli("show interface")
2699a6dafd5SPaul Vinciguerra        self.assertNotIn('BondEthernet0', ifs)
2709cd2d7a5SSteven
2719cd2d7a5SSteven        # verify BondEthernet0 is not in the dump
2729cd2d7a5SSteven        if_dump = self.vapi.sw_interface_bond_dump()
2739cd2d7a5SSteven        self.assertFalse(bond0.is_interface_config_in_dump(if_dump))
2749cd2d7a5SSteven
275a1876b84SSteven Luong
2769cd2d7a5SStevenif __name__ == '__main__':
2779cd2d7a5SSteven    unittest.main(testRunner=VppTestRunner)
278