1#!/usr/bin/env python3
2
3import socket
4import unittest
5
6from framework import VppTestCase, VppTestRunner
7from scapy.packet import Raw
8from scapy.layers.l2 import Ether
9from scapy.layers.inet import IP, UDP
10from vpp_bond_interface import VppBondInterface
11from vpp_papi import MACAddress
12
13
14class TestBondInterface(VppTestCase):
15    """Bond Test Case
16
17    """
18
19    @classmethod
20    def setUpClass(cls):
21        super(TestBondInterface, cls).setUpClass()
22        # Test variables
23        cls.pkts_per_burst = 257    # Number of packets per burst
24        # create 3 pg interfaces
25        cls.create_pg_interfaces(range(4))
26
27        # packet sizes
28        cls.pg_if_packet_sizes = [64, 512, 1518]  # , 9018]
29
30        # setup all interfaces
31        for i in cls.pg_interfaces:
32            i.admin_up()
33
34    @classmethod
35    def tearDownClass(cls):
36        super(TestBondInterface, cls).tearDownClass()
37
38    def setUp(self):
39        super(TestBondInterface, self).setUp()
40
41    def tearDown(self):
42        super(TestBondInterface, self).tearDown()
43
44    def show_commands_at_teardown(self):
45        self.logger.info(self.vapi.ppcli("show interface"))
46
47    def test_bond_traffic(self):
48        """ Bond traffic test """
49
50        # topology
51        #
52        # RX->              TX->
53        #
54        # pg2 ------+        +------pg0 (slave)
55        #           |        |
56        #          BondEthernet0 (10.10.10.1)
57        #           |        |
58        # pg3 ------+        +------pg1 (slave)
59        #
60
61        # create interface (BondEthernet0)
62        #        self.logger.info("create bond")
63        bond0_mac = "02:fe:38:30:59:3c"
64        mac = MACAddress(bond0_mac).packed
65        bond0 = VppBondInterface(self,
66                                 mode=3,
67                                 lb=1,
68                                 numa_only=0,
69                                 use_custom_mac=1,
70                                 mac_address=mac)
71        bond0.add_vpp_config()
72        bond0.admin_up()
73        self.vapi.sw_interface_add_del_address(
74            sw_if_index=bond0.sw_if_index,
75            prefix="10.10.10.1/24")
76
77        self.pg2.config_ip4()
78        self.pg2.resolve_arp()
79        self.pg3.config_ip4()
80        self.pg3.resolve_arp()
81
82        self.logger.info(self.vapi.cli("show interface"))
83        self.logger.info(self.vapi.cli("show interface address"))
84        self.logger.info(self.vapi.cli("show ip neighbors"))
85
86        # enslave pg0 and pg1 to BondEthernet0
87        self.logger.info("bond enslave interface pg0 to BondEthernet0")
88        bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index)
89        self.logger.info("bond enslave interface pg1 to BondEthernet0")
90        bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index)
91
92        # verify both slaves in BondEthernet0
93        if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
94        self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
95        self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
96
97        # generate a packet from pg2 -> BondEthernet0 -> pg1
98        # BondEthernet0 TX hashes this packet to pg1
99        p2 = (Ether(src=bond0_mac, dst=self.pg2.local_mac) /
100              IP(src=self.pg2.local_ip4, dst="10.10.10.12") /
101              UDP(sport=1235, dport=1235) /
102              Raw(b'\xa5' * 100))
103        self.pg2.add_stream(p2)
104
105        # generate a packet from pg3 -> BondEthernet0 -> pg0
106        # BondEthernet0 TX hashes this packet to pg0
107        # notice the ip address and ports are different than p2 packet
108        p3 = (Ether(src=bond0_mac, dst=self.pg3.local_mac) /
109              IP(src=self.pg3.local_ip4, dst="10.10.10.11") /
110              UDP(sport=1234, dport=1234) /
111              Raw(b'\xa5' * 100))
112        self.pg3.add_stream(p3)
113
114        self.pg_enable_capture(self.pg_interfaces)
115
116        # set up the static arp entries pointing to the BondEthernet0 interface
117        # so that it does not try to resolve the ip address
118        self.logger.info(self.vapi.cli(
119            "set ip neighbor static BondEthernet0 10.10.10.12 abcd.abcd.0002"))
120        self.logger.info(self.vapi.cli(
121            "set ip neighbor static BondEthernet0 10.10.10.11 abcd.abcd.0004"))
122
123        # clear the interface counters
124        self.logger.info(self.vapi.cli("clear interfaces"))
125
126        self.pg_start()
127
128        self.logger.info("check the interface counters")
129
130        # verify counters
131
132        # BondEthernet0 tx bytes = 284
133        intfs = self.vapi.cli("show interface BondEthernet0").split("\n")
134        found = 0
135        for intf in intfs:
136            if "tx bytes" in intf and "284" in intf:
137                found = 1
138        self.assertEqual(found, 1)
139
140        # BondEthernet0 tx bytes = 284
141        intfs = self.vapi.cli("show interface BondEthernet0").split("\n")
142        found = 0
143        for intf in intfs:
144            if "tx bytes" in intf and "284" in intf:
145                found = 1
146        self.assertEqual(found, 1)
147
148        # pg2 rx bytes = 142
149        intfs = self.vapi.cli("show interface pg2").split("\n")
150        found = 0
151        for intf in intfs:
152            if "rx bytes" in intf and "142" in intf:
153                found = 1
154        self.assertEqual(found, 1)
155
156        # pg3 rx bytes = 142
157        intfs = self.vapi.cli("show interface pg3").split("\n")
158        found = 0
159        for intf in intfs:
160            if "rx bytes" in intf and "142" in intf:
161                found = 1
162        self.assertEqual(found, 1)
163
164        bond0.remove_vpp_config()
165
166    def test_bond_enslave(self):
167        """ Bond enslave/detach slave test """
168
169        # create interface (BondEthernet0) and set bond mode to LACP
170        self.logger.info("create bond")
171        bond0 = VppBondInterface(self, mode=5)
172        bond0.add_vpp_config()
173        bond0.admin_up()
174
175        # verify that interfaces can be enslaved and detached two times
176        for i in range(2):
177            # verify pg0 and pg1 not in BondEthernet0
178            if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
179            self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
180            self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
181
182            # enslave pg0 and pg1 to BondEthernet0
183            self.logger.info("bond enslave interface pg0 to BondEthernet0")
184            bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index,
185                                             is_passive=0,
186                                             is_long_timeout=0)
187
188            self.logger.info("bond enslave interface pg1 to BondEthernet0")
189            bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index,
190                                             is_passive=0,
191                                             is_long_timeout=0)
192            # verify both slaves in BondEthernet0
193            if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
194            self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
195            self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
196
197            # detach interface pg0
198            self.logger.info("detach interface pg0")
199            bond0.detach_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index)
200
201            # verify pg0 is not in BondEthernet0, but pg1 is
202            if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
203            self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
204            self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
205
206            # detach interface pg1
207            self.logger.info("detach interface pg1")
208            bond0.detach_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index)
209
210            # verify pg0 and pg1 not in BondEthernet0
211            if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
212            self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
213            self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
214
215        bond0.remove_vpp_config()
216
217    def test_bond(self):
218        """ Bond add/delete interface test """
219        self.logger.info("Bond add interfaces")
220
221        # create interface 1 (BondEthernet0)
222        bond0 = VppBondInterface(self, mode=5)
223        bond0.add_vpp_config()
224        bond0.admin_up()
225
226        # create interface 2 (BondEthernet1)
227        bond1 = VppBondInterface(self, mode=3)
228        bond1.add_vpp_config()
229        bond1.admin_up()
230
231        # verify both interfaces in the show
232        ifs = self.vapi.cli("show interface")
233        self.assertIn('BondEthernet0', ifs)
234        self.assertIn('BondEthernet1', ifs)
235
236        # verify they are in the dump also
237        if_dump = self.vapi.sw_interface_bond_dump()
238        self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
239        self.assertTrue(bond1.is_interface_config_in_dump(if_dump))
240
241        # delete BondEthernet1
242        self.logger.info("Deleting BondEthernet1")
243        bond1.remove_vpp_config()
244
245        self.logger.info("Verifying BondEthernet1 is deleted")
246
247        ifs = self.vapi.cli("show interface")
248        # verify BondEthernet0 still in the show
249        self.assertIn('BondEthernet0', ifs)
250
251        # verify BondEthernet1 not in the show
252        self.assertNotIn('BondEthernet1', ifs)
253
254        # verify BondEthernet1 is not in the dump
255        if_dump = self.vapi.sw_interface_bond_dump()
256        self.assertFalse(bond1.is_interface_config_in_dump(if_dump))
257
258        # verify BondEthernet0 is still in the dump
259        self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
260
261        # delete BondEthernet0
262        self.logger.info("Deleting BondEthernet0")
263        bond0.remove_vpp_config()
264
265        self.logger.info("Verifying BondEthernet0 is deleted")
266
267        # verify BondEthernet0 not in the show
268        ifs = self.vapi.cli("show interface")
269        self.assertNotIn('BondEthernet0', ifs)
270
271        # verify BondEthernet0 is not in the dump
272        if_dump = self.vapi.sw_interface_bond_dump()
273        self.assertFalse(bond0.is_interface_config_in_dump(if_dump))
274
275
276if __name__ == '__main__':
277    unittest.main(testRunner=VppTestRunner)
278