1#!/usr/bin/env python3
2
3import unittest
4import socket
5
6from framework import VppTestCase, VppTestRunner
7from vpp_ip_route import VppIpRoute, VppRoutePath
8from vpp_l2 import L2_PORT_TYPE, BRIDGE_FLAGS
9
10from scapy.packet import Raw
11from scapy.layers.l2 import Ether
12from scapy.layers.inet import IP, UDP
13
14NUM_PKTS = 67
15
16
17class TestL2Flood(VppTestCase):
18    """ L2-flood """
19
20    @classmethod
21    def setUpClass(cls):
22        super(TestL2Flood, cls).setUpClass()
23
24    @classmethod
25    def tearDownClass(cls):
26        super(TestL2Flood, cls).tearDownClass()
27
28    def setUp(self):
29        super(TestL2Flood, self).setUp()
30
31        # 12 l2 interface and one l3
32        self.create_pg_interfaces(range(13))
33        self.create_bvi_interfaces(1)
34
35        for i in self.pg_interfaces:
36            i.admin_up()
37        for i in self.bvi_interfaces:
38            i.admin_up()
39
40        self.pg12.config_ip4()
41        self.pg12.resolve_arp()
42        self.bvi0.config_ip4()
43
44    def tearDown(self):
45        self.pg12.unconfig_ip4()
46        self.bvi0.unconfig_ip4()
47
48        for i in self.pg_interfaces:
49            i.admin_down()
50        for i in self.bvi_interfaces:
51            i.admin_down()
52        super(TestL2Flood, self).tearDown()
53
54    def test_flood(self):
55        """ L2 Flood Tests """
56
57        #
58        # Create a single bridge Domain
59        #
60        self.vapi.bridge_domain_add_del(bd_id=1)
61
62        #
63        # add each interface to the BD. 3 interfaces per split horizon group
64        #
65        for i in self.pg_interfaces[0:4]:
66            self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
67                                                 bd_id=1, shg=0)
68        for i in self.pg_interfaces[4:8]:
69            self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
70                                                 bd_id=1, shg=1)
71        for i in self.pg_interfaces[8:12]:
72            self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
73                                                 bd_id=1, shg=2)
74        for i in self.bvi_interfaces:
75            self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
76                                                 bd_id=1, shg=2,
77                                                 port_type=L2_PORT_TYPE.BVI)
78
79        p = (Ether(dst="ff:ff:ff:ff:ff:ff",
80                   src="00:00:de:ad:be:ef") /
81             IP(src="10.10.10.10", dst="1.1.1.1") /
82             UDP(sport=1234, dport=1234) /
83             Raw(b'\xa5' * 100))
84
85        #
86        # input on pg0 expect copies on pg1->11
87        # this is in SHG=0 so its flooded to all, expect the pg0 since that's
88        # the ingress link
89        #
90        self.pg0.add_stream(p*NUM_PKTS)
91        self.pg_enable_capture(self.pg_interfaces)
92        self.pg_start()
93
94        for i in self.pg_interfaces[1:12]:
95            rx0 = i.get_capture(NUM_PKTS, timeout=1)
96
97        #
98        # input on pg4 (SHG=1) expect copies on pg0->3 (SHG=0)
99        # and pg8->11 (SHG=2)
100        #
101        self.pg4.add_stream(p*NUM_PKTS)
102        self.pg_enable_capture(self.pg_interfaces)
103        self.pg_start()
104
105        for i in self.pg_interfaces[:4]:
106            rx0 = i.get_capture(NUM_PKTS, timeout=1)
107        for i in self.pg_interfaces[8:12]:
108            rx0 = i.get_capture(NUM_PKTS, timeout=1)
109        for i in self.pg_interfaces[4:8]:
110            i.assert_nothing_captured(remark="Different SH group")
111
112        #
113        # An IP route so the packet that hits the BVI is sent out of pg12
114        #
115        ip_route = VppIpRoute(self, "1.1.1.1", 32,
116                              [VppRoutePath(self.pg12.remote_ip4,
117                                            self.pg12.sw_if_index)])
118        ip_route.add_vpp_config()
119
120        self.logger.info(self.vapi.cli("sh bridge 1 detail"))
121
122        #
123        # input on pg0 expect copies on pg1->12
124        # this is in SHG=0 so its flooded to all, expect the pg0 since that's
125        # the ingress link
126        #
127        self.pg0.add_stream(p*NUM_PKTS)
128        self.pg_enable_capture(self.pg_interfaces)
129        self.pg_start()
130
131        for i in self.pg_interfaces[1:]:
132            rx0 = i.get_capture(NUM_PKTS, timeout=1)
133
134        #
135        # input on pg4 (SHG=1) expect copies on pg0->3 (SHG=0)
136        # and pg8->12 (SHG=2)
137        #
138        self.pg4.add_stream(p*NUM_PKTS)
139        self.pg_enable_capture(self.pg_interfaces)
140        self.pg_start()
141
142        for i in self.pg_interfaces[:4]:
143            rx0 = i.get_capture(NUM_PKTS, timeout=1)
144        for i in self.pg_interfaces[8:13]:
145            rx0 = i.get_capture(NUM_PKTS, timeout=1)
146        for i in self.pg_interfaces[4:8]:
147            i.assert_nothing_captured(remark="Different SH group")
148
149        #
150        # cleanup
151        #
152        for i in self.pg_interfaces[:12]:
153            self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
154                                                 bd_id=1, enable=0)
155        for i in self.bvi_interfaces:
156            self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
157                                                 bd_id=1, shg=2,
158                                                 port_type=L2_PORT_TYPE.BVI,
159                                                 enable=0)
160
161        self.vapi.bridge_domain_add_del(bd_id=1, is_add=0)
162
163    def test_flood_one(self):
164        """ L2 no-Flood Test """
165
166        #
167        # Create a single bridge Domain
168        #
169        self.vapi.bridge_domain_add_del(bd_id=1)
170
171        #
172        # add 2 interfaces to the BD. this means a flood goes to only
173        # one member
174        #
175        for i in self.pg_interfaces[:2]:
176            self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
177                                                 bd_id=1, shg=0)
178
179        p = (Ether(dst="ff:ff:ff:ff:ff:ff",
180                   src="00:00:de:ad:be:ef") /
181             IP(src="10.10.10.10", dst="1.1.1.1") /
182             UDP(sport=1234, dport=1234) /
183             Raw(b'\xa5' * 100))
184
185        #
186        # input on pg0 expect copies on pg1
187        #
188        self.send_and_expect(self.pg0, p*NUM_PKTS, self.pg1)
189
190        #
191        # cleanup
192        #
193        for i in self.pg_interfaces[:2]:
194            self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
195                                                 bd_id=1, enable=0)
196        self.vapi.bridge_domain_add_del(bd_id=1, is_add=0)
197
198    def test_uu_fwd(self):
199        """ UU Flood """
200
201        #
202        # Create a single bridge Domain
203        #
204        self.vapi.bridge_domain_add_del(bd_id=1, uu_flood=1)
205
206        #
207        # add each interface to the BD. 3 interfaces per split horizon group
208        #
209        for i in self.pg_interfaces[0:4]:
210            self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
211                                                 bd_id=1, shg=0)
212
213        #
214        # an unknown unicast and broadcast packets
215        #
216        p_uu = (Ether(dst="00:00:00:c1:5c:00",
217                      src="00:00:de:ad:be:ef") /
218                IP(src="10.10.10.10", dst="1.1.1.1") /
219                UDP(sport=1234, dport=1234) /
220                Raw(b'\xa5' * 100))
221        p_bm = (Ether(dst="ff:ff:ff:ff:ff:ff",
222                      src="00:00:de:ad:be:ef") /
223                IP(src="10.10.10.10", dst="1.1.1.1") /
224                UDP(sport=1234, dport=1234) /
225                Raw(b'\xa5' * 100))
226
227        #
228        # input on pg0, expected copies on pg1->4
229        #
230        self.pg0.add_stream(p_uu*NUM_PKTS)
231        self.pg_enable_capture(self.pg_interfaces)
232        self.pg_start()
233
234        for i in self.pg_interfaces[1:4]:
235            rx0 = i.get_capture(NUM_PKTS, timeout=1)
236
237        self.pg0.add_stream(p_bm*NUM_PKTS)
238        self.pg_enable_capture(self.pg_interfaces)
239        self.pg_start()
240
241        for i in self.pg_interfaces[1:4]:
242            rx0 = i.get_capture(NUM_PKTS, timeout=1)
243
244        #
245        # use pg8 as the uu-fwd interface
246        #
247        self.vapi.sw_interface_set_l2_bridge(
248            rx_sw_if_index=self.pg8.sw_if_index, bd_id=1, shg=0,
249            port_type=L2_PORT_TYPE.UU_FWD)
250
251        #
252        # expect the UU packet on the uu-fwd interface and not be flooded
253        #
254        self.pg0.add_stream(p_uu*NUM_PKTS)
255        self.pg_enable_capture(self.pg_interfaces)
256        self.pg_start()
257
258        rx0 = self.pg8.get_capture(NUM_PKTS, timeout=1)
259
260        for i in self.pg_interfaces[0:4]:
261            i.assert_nothing_captured(remark="UU not flooded")
262
263        self.pg0.add_stream(p_bm*NUM_PKTS)
264        self.pg_enable_capture(self.pg_interfaces)
265        self.pg_start()
266
267        for i in self.pg_interfaces[1:4]:
268            rx0 = i.get_capture(NUM_PKTS, timeout=1)
269
270        #
271        # remove the uu-fwd interface and expect UU to be flooded again
272        #
273        self.vapi.sw_interface_set_l2_bridge(
274            rx_sw_if_index=self.pg8.sw_if_index, bd_id=1, shg=0,
275            port_type=L2_PORT_TYPE.UU_FWD, enable=0)
276
277        self.pg0.add_stream(p_uu*NUM_PKTS)
278        self.pg_enable_capture(self.pg_interfaces)
279        self.pg_start()
280
281        for i in self.pg_interfaces[1:4]:
282            rx0 = i.get_capture(NUM_PKTS, timeout=1)
283
284        #
285        # change the BD config to not support UU-flood
286        #
287        self.vapi.bridge_flags(bd_id=1, is_set=0, flags=BRIDGE_FLAGS.UU_FLOOD)
288
289        self.send_and_assert_no_replies(self.pg0, p_uu)
290
291        #
292        # re-add the uu-fwd interface
293        #
294        self.vapi.sw_interface_set_l2_bridge(
295            rx_sw_if_index=self.pg8.sw_if_index, bd_id=1, shg=0,
296            port_type=L2_PORT_TYPE.UU_FWD)
297        self.logger.info(self.vapi.cli("sh bridge 1 detail"))
298
299        self.pg0.add_stream(p_uu*NUM_PKTS)
300        self.pg_enable_capture(self.pg_interfaces)
301        self.pg_start()
302
303        rx0 = self.pg8.get_capture(NUM_PKTS, timeout=1)
304
305        for i in self.pg_interfaces[0:4]:
306            i.assert_nothing_captured(remark="UU not flooded")
307
308        #
309        # remove the uu-fwd interface
310        #
311        self.vapi.sw_interface_set_l2_bridge(
312            rx_sw_if_index=self.pg8.sw_if_index, bd_id=1, shg=0,
313            port_type=L2_PORT_TYPE.UU_FWD, enable=0)
314        self.send_and_assert_no_replies(self.pg0, p_uu)
315
316        #
317        # cleanup
318        #
319        for i in self.pg_interfaces[:4]:
320            self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
321                                                 bd_id=1, enable=0)
322
323        self.vapi.bridge_domain_add_del(bd_id=1, is_add=0)
324
325
326if __name__ == '__main__':
327    unittest.main(testRunner=VppTestRunner)
328