make test: fix missing log/packet messages
[vpp.git] / test / test_l2bd.py
1 #!/usr/bin/env python
2
3 import unittest
4 import random
5
6 from scapy.packet import Raw
7 from scapy.layers.l2 import Ether, Dot1Q
8 from scapy.layers.inet import IP, UDP
9
10 from framework import VppTestCase, VppTestRunner
11 from util import Host, ppp
12 from vpp_sub_interface import VppDot1QSubint, VppDot1ADSubint
13
14
15 class TestL2bd(VppTestCase):
16     """ L2BD Test Case """
17
18     @classmethod
19     def setUpClass(cls):
20         """
21         Perform standard class setup (defined by class method setUpClass in
22         class VppTestCase) before running the test case, set test case related
23         variables and configure VPP.
24
25         :var int bd_id: Bridge domain ID.
26         :var int mac_entries_count: Number of MAC entries for bridge-domain to
27             learn.
28         :var int dot1q_tag: VLAN tag for dot1q sub-interface.
29         :var int dot1ad_sub_id: SubID of dot1ad sub-interface.
30         :var int dot1ad_outer_tag: VLAN S-tag for dot1ad sub-interface.
31         :var int dot1ad_inner_tag: VLAN C-tag for dot1ad sub-interface.
32         :var int sl_pkts_per_burst: Number of packets in burst for single-loop
33             test.
34         :var int dl_pkts_per_burst: Number of packets in burst for dual-loop
35             test.
36         """
37         super(TestL2bd, cls).setUpClass()
38
39         # Test variables
40         cls.bd_id = 1
41         cls.mac_entries_count = 100
42         # cls.dot1q_sub_id = 100
43         cls.dot1q_tag = 100
44         cls.dot1ad_sub_id = 20
45         cls.dot1ad_outer_tag = 200
46         cls.dot1ad_inner_tag = 300
47         cls.sl_pkts_per_burst = 2
48         cls.dl_pkts_per_burst = 257
49
50         try:
51             # create 3 pg interfaces
52             cls.create_pg_interfaces(range(3))
53
54             # create 2 sub-interfaces for pg1 and pg2
55             cls.sub_interfaces = [
56                 VppDot1QSubint(cls, cls.pg1, cls.dot1q_tag),
57                 VppDot1ADSubint(cls, cls.pg2, cls.dot1ad_sub_id,
58                                 cls.dot1ad_outer_tag, cls.dot1ad_inner_tag)]
59
60             # packet flows mapping pg0 -> pg1, pg2, etc.
61             cls.flows = dict()
62             cls.flows[cls.pg0] = [cls.pg1, cls.pg2]
63             cls.flows[cls.pg1] = [cls.pg0, cls.pg2]
64             cls.flows[cls.pg2] = [cls.pg0, cls.pg1]
65
66             # packet sizes
67             cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
68             cls.sub_if_packet_sizes = [64, 512, 1518 + 4, 9018 + 4]
69
70             cls.interfaces = list(cls.pg_interfaces)
71             cls.interfaces.extend(cls.sub_interfaces)
72
73             # Create BD with MAC learning enabled and put interfaces and
74             #  sub-interfaces to this BD
75             for pg_if in cls.pg_interfaces:
76                 sw_if_index = pg_if.sub_if.sw_if_index \
77                     if hasattr(pg_if, 'sub_if') else pg_if.sw_if_index
78                 cls.vapi.sw_interface_set_l2_bridge(sw_if_index,
79                                                     bd_id=cls.bd_id)
80
81             # setup all interfaces
82             for i in cls.interfaces:
83                 i.admin_up()
84
85             # mapping between packet-generator index and lists of test hosts
86             cls.hosts_by_pg_idx = dict()
87
88             # create test host entries and inject packets to learn MAC entries
89             # in the bridge-domain
90             cls.create_hosts_and_learn(cls.mac_entries_count)
91             cls.logger.info(cls.vapi.ppcli("show l2fib"))
92
93         except Exception:
94             super(TestL2bd, cls).tearDownClass()
95             raise
96
97     def setUp(self):
98         """
99         Clear trace and packet infos before running each test.
100         """
101         super(TestL2bd, self).setUp()
102         self.packet_infos = {}
103
104     def tearDown(self):
105         """
106         Show various debug prints after each test.
107         """
108         super(TestL2bd, self).tearDown()
109         if not self.vpp_dead:
110             self.logger.info(self.vapi.ppcli("show l2fib verbose"))
111             self.logger.info(self.vapi.ppcli("show bridge-domain %s detail" %
112                                              self.bd_id))
113
114     @classmethod
115     def create_hosts_and_learn(cls, count):
116         """
117         Create required number of host MAC addresses and distribute them among
118         interfaces. Create host IPv4 address for every host MAC address. Create
119         L2 MAC packet stream with host MAC addresses per interface to let
120         the bridge domain learn these MAC addresses.
121
122         :param count: Integer number of hosts to create MAC/IPv4 addresses for.
123         """
124         n_int = len(cls.pg_interfaces)
125         macs_per_if = count / n_int
126         i = -1
127         for pg_if in cls.pg_interfaces:
128             i += 1
129             start_nr = macs_per_if * i
130             end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
131             cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
132             hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
133             packets = []
134             for j in range(start_nr, end_nr):
135                 host = Host(
136                     "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
137                     "172.17.1%02x.%u" % (pg_if.sw_if_index, j))
138                 packet = (Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac))
139                 hosts.append(host)
140                 if hasattr(pg_if, 'sub_if'):
141                     packet = pg_if.sub_if.add_dot1_layer(packet)
142                 packets.append(packet)
143             pg_if.add_stream(packets)
144         cls.logger.info("Sending broadcast eth frames for MAC learning")
145         cls.pg_start()
146
147     def create_stream(self, src_if, packet_sizes, packets_per_burst):
148         """
149         Create input packet stream for defined interface.
150
151         :param object src_if: Interface to create packet stream for.
152         :param list packet_sizes: List of required packet sizes.
153         :param int packets_per_burst: Number of packets in burst.
154         :return: Stream of packets.
155         """
156         pkts = []
157         for i in range(0, packets_per_burst):
158             dst_if = self.flows[src_if][i % 2]
159             dst_host = random.choice(self.hosts_by_pg_idx[dst_if.sw_if_index])
160             src_host = random.choice(self.hosts_by_pg_idx[src_if.sw_if_index])
161             pkt_info = self.create_packet_info(
162                 src_if.sw_if_index, dst_if.sw_if_index)
163             payload = self.info_to_payload(pkt_info)
164             p = (Ether(dst=dst_host.mac, src=src_host.mac) /
165                  IP(src=src_host.ip4, dst=dst_host.ip4) /
166                  UDP(sport=1234, dport=1234) /
167                  Raw(payload))
168             pkt_info.data = p.copy()
169             if hasattr(src_if, 'sub_if'):
170                 p = src_if.sub_if.add_dot1_layer(p)
171             size = random.choice(packet_sizes)
172             self.extend_packet(p, size)
173             pkts.append(p)
174         return pkts
175
176     def verify_capture(self, pg_if, capture):
177         """
178         Verify captured input packet stream for defined interface.
179
180         :param object pg_if: Interface to verify captured packet stream for.
181         :param list capture: Captured packet stream.
182         """
183         last_info = dict()
184         for i in self.pg_interfaces:
185             last_info[i.sw_if_index] = None
186         dst_sw_if_index = pg_if.sw_if_index
187         for packet in capture:
188             payload_info = self.payload_to_info(str(packet[Raw]))
189             src_sw_if_index = payload_info.src
190             src_if = None
191             for ifc in self.pg_interfaces:
192                 if ifc != pg_if:
193                     if ifc.sw_if_index == src_sw_if_index:
194                         src_if = ifc
195                         break
196             if hasattr(src_if, 'sub_if'):
197                 # Check VLAN tags and Ethernet header
198                 packet = src_if.sub_if.remove_dot1_layer(packet)
199             self.assertTrue(Dot1Q not in packet)
200             try:
201                 ip = packet[IP]
202                 udp = packet[UDP]
203                 packet_index = payload_info.index
204                 self.assertEqual(payload_info.dst, dst_sw_if_index)
205                 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
206                                   (pg_if.name, payload_info.src, packet_index))
207                 next_info = self.get_next_packet_info_for_interface2(
208                     payload_info.src, dst_sw_if_index,
209                     last_info[payload_info.src])
210                 last_info[payload_info.src] = next_info
211                 self.assertTrue(next_info is not None)
212                 self.assertEqual(packet_index, next_info.index)
213                 saved_packet = next_info.data
214                 # Check standard fields
215                 self.assertEqual(ip.src, saved_packet[IP].src)
216                 self.assertEqual(ip.dst, saved_packet[IP].dst)
217                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
218                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
219             except:
220                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
221                 raise
222         for i in self.pg_interfaces:
223             remaining_packet = self.get_next_packet_info_for_interface2(
224                 i, dst_sw_if_index, last_info[i.sw_if_index])
225             self.assertTrue(
226                 remaining_packet is None,
227                 "Port %u: Packet expected from source %u didn't arrive" %
228                 (dst_sw_if_index, i.sw_if_index))
229
230     def run_l2bd_test(self, pkts_per_burst):
231         """ L2BD MAC learning test """
232
233         # Create incoming packet streams for packet-generator interfaces
234         for i in self.pg_interfaces:
235             packet_sizes = self.sub_if_packet_sizes if hasattr(i, 'sub_if') \
236                 else self.pg_if_packet_sizes
237             pkts = self.create_stream(i, packet_sizes, pkts_per_burst)
238             i.add_stream(pkts)
239
240         # Enable packet capture and start packet sending
241         self.pg_enable_capture(self.pg_interfaces)
242         self.pg_start()
243
244         # Verify outgoing packet streams per packet-generator interface
245         for i in self.pg_interfaces:
246             capture = i.get_capture()
247             self.logger.info("Verifying capture on interface %s" % i.name)
248             self.verify_capture(i, capture)
249
250     def test_l2bd_sl(self):
251         """ L2BD MAC learning single-loop test
252
253         Test scenario:
254             1.config
255                 MAC learning enabled
256                 learn 100 MAC enries
257                 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of
258                 dot1ad in the first version)
259
260             2.sending l2 eth pkts between 3 interface
261                 64B, 512B, 1518B, 9200B (ether_size)
262                 burst of 2 pkts per interface
263         """
264
265         self.run_l2bd_test(self.sl_pkts_per_burst)
266
267     def test_l2bd_dl(self):
268         """ L2BD MAC learning dual-loop test
269
270          Test scenario:
271             1.config
272                 MAC learning enabled
273                 learn 100 MAC enries
274                 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of
275                 dot1ad in the first version)
276
277             2.sending l2 eth pkts between 3 interface
278                 64B, 512B, 1518B, 9200B (ether_size)
279                 burst of 257 pkts per interface
280         """
281
282         self.run_l2bd_test(self.dl_pkts_per_burst)
283
284
285 if __name__ == '__main__':
286     unittest.main(testRunner=VppTestRunner)