blob: 718d5126bd990f481688a428f6b086a6b0a0501f [file] [log] [blame]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001#!/usr/bin/env python
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
3import unittest
4import random
5
Klement Sekeraf62ae122016-10-11 11:47:09 +02006from scapy.packet import Raw
7from scapy.layers.l2 import Ether, Dot1Q
8from scapy.layers.inet import IP, UDP
9
10from framework import VppTestCase, VppTestRunner
Klement Sekera7bb873a2016-11-18 07:38:42 +010011from util import Host, ppp
Jan49c0fca2016-10-26 15:44:27 +020012from vpp_sub_interface import VppDot1QSubint, VppDot1ADSubint
Damjan Marionf56b77a2016-10-03 19:44:57 +020013
14
Damjan Marionf56b77a2016-10-03 19:44:57 +020015class TestL2bd(VppTestCase):
16 """ L2BD Test Case """
17
Damjan Marionf56b77a2016-10-03 19:44:57 +020018 @classmethod
19 def setUpClass(cls):
Jan49c0fca2016-10-26 15:44:27 +020020 """
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 """
Damjan Marionf56b77a2016-10-03 19:44:57 +020037 super(TestL2bd, cls).setUpClass()
38
Jan49c0fca2016-10-26 15:44:27 +020039 # 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
Ole Troana5b2eec2019-03-11 19:23:25 +010078 cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=sw_if_index,
Jan49c0fca2016-10-26 15:44:27 +020079 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
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070097 @classmethod
98 def tearDownClass(cls):
99 super(TestL2bd, cls).tearDownClass()
100
Klement Sekeraf62ae122016-10-11 11:47:09 +0200101 def setUp(self):
Jan49c0fca2016-10-26 15:44:27 +0200102 """
103 Clear trace and packet infos before running each test.
104 """
Klement Sekeraf62ae122016-10-11 11:47:09 +0200105 super(TestL2bd, self).setUp()
Klement Sekeradab231a2016-12-21 08:50:14 +0100106 self.reset_packet_infos()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200107
Damjan Marionf56b77a2016-10-03 19:44:57 +0200108 def tearDown(self):
Jan49c0fca2016-10-26 15:44:27 +0200109 """
110 Show various debug prints after each test.
111 """
Klement Sekeraf62ae122016-10-11 11:47:09 +0200112 super(TestL2bd, self).tearDown()
113 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200114 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
115 self.logger.info(self.vapi.ppcli("show bridge-domain %s detail" %
Klement Sekera7bb873a2016-11-18 07:38:42 +0100116 self.bd_id))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200117
Jan49c0fca2016-10-26 15:44:27 +0200118 @classmethod
119 def create_hosts_and_learn(cls, count):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200120 """
121 Create required number of host MAC addresses and distribute them among
122 interfaces. Create host IPv4 address for every host MAC address. Create
123 L2 MAC packet stream with host MAC addresses per interface to let
124 the bridge domain learn these MAC addresses.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200125
Klement Sekeraf62ae122016-10-11 11:47:09 +0200126 :param count: Integer number of hosts to create MAC/IPv4 addresses for.
127 """
Jan49c0fca2016-10-26 15:44:27 +0200128 n_int = len(cls.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200129 macs_per_if = count / n_int
Klement Sekeraf62ae122016-10-11 11:47:09 +0200130 i = -1
Jan49c0fca2016-10-26 15:44:27 +0200131 for pg_if in cls.pg_interfaces:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200132 i += 1
133 start_nr = macs_per_if * i
134 end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
Jan49c0fca2016-10-26 15:44:27 +0200135 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
136 hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200137 packets = []
138 for j in range(start_nr, end_nr):
Matej Klotton0178d522016-11-04 11:11:44 +0100139 host = Host(
Klement Sekeraf62ae122016-10-11 11:47:09 +0200140 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
141 "172.17.1%02x.%u" % (pg_if.sw_if_index, j))
142 packet = (Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac))
143 hosts.append(host)
144 if hasattr(pg_if, 'sub_if'):
145 packet = pg_if.sub_if.add_dot1_layer(packet)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200146 packets.append(packet)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200147 pg_if.add_stream(packets)
Jan49c0fca2016-10-26 15:44:27 +0200148 cls.logger.info("Sending broadcast eth frames for MAC learning")
149 cls.pg_start()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200150
Jan49c0fca2016-10-26 15:44:27 +0200151 def create_stream(self, src_if, packet_sizes, packets_per_burst):
152 """
153 Create input packet stream for defined interface.
154
155 :param object src_if: Interface to create packet stream for.
156 :param list packet_sizes: List of required packet sizes.
157 :param int packets_per_burst: Number of packets in burst.
158 :return: Stream of packets.
159 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200160 pkts = []
Jan49c0fca2016-10-26 15:44:27 +0200161 for i in range(0, packets_per_burst):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200162 dst_if = self.flows[src_if][i % 2]
163 dst_host = random.choice(self.hosts_by_pg_idx[dst_if.sw_if_index])
164 src_host = random.choice(self.hosts_by_pg_idx[src_if.sw_if_index])
Klement Sekeradab231a2016-12-21 08:50:14 +0100165 pkt_info = self.create_packet_info(src_if, dst_if)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200166 payload = self.info_to_payload(pkt_info)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200167 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
168 IP(src=src_host.ip4, dst=dst_host.ip4) /
Damjan Marionf56b77a2016-10-03 19:44:57 +0200169 UDP(sport=1234, dport=1234) /
170 Raw(payload))
171 pkt_info.data = p.copy()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200172 if hasattr(src_if, 'sub_if'):
173 p = src_if.sub_if.add_dot1_layer(p)
Jan49c0fca2016-10-26 15:44:27 +0200174 size = random.choice(packet_sizes)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200175 self.extend_packet(p, size)
176 pkts.append(p)
177 return pkts
Damjan Marionf56b77a2016-10-03 19:44:57 +0200178
Klement Sekeraf62ae122016-10-11 11:47:09 +0200179 def verify_capture(self, pg_if, capture):
Jan49c0fca2016-10-26 15:44:27 +0200180 """
181 Verify captured input packet stream for defined interface.
182
183 :param object pg_if: Interface to verify captured packet stream for.
184 :param list capture: Captured packet stream.
185 """
Klement Sekeraf62ae122016-10-11 11:47:09 +0200186 last_info = dict()
187 for i in self.pg_interfaces:
188 last_info[i.sw_if_index] = None
189 dst_sw_if_index = pg_if.sw_if_index
Damjan Marionf56b77a2016-10-03 19:44:57 +0200190 for packet in capture:
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800191 payload_info = self.payload_to_info(packet[Raw])
Klement Sekeraf62ae122016-10-11 11:47:09 +0200192 src_sw_if_index = payload_info.src
193 src_if = None
194 for ifc in self.pg_interfaces:
195 if ifc != pg_if:
196 if ifc.sw_if_index == src_sw_if_index:
197 src_if = ifc
198 break
199 if hasattr(src_if, 'sub_if'):
200 # Check VLAN tags and Ethernet header
201 packet = src_if.sub_if.remove_dot1_layer(packet)
202 self.assertTrue(Dot1Q not in packet)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200203 try:
204 ip = packet[IP]
205 udp = packet[UDP]
Klement Sekeraf62ae122016-10-11 11:47:09 +0200206 packet_index = payload_info.index
207 self.assertEqual(payload_info.dst, dst_sw_if_index)
Jan49c0fca2016-10-26 15:44:27 +0200208 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
209 (pg_if.name, payload_info.src, packet_index))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200210 next_info = self.get_next_packet_info_for_interface2(
Klement Sekeraf62ae122016-10-11 11:47:09 +0200211 payload_info.src, dst_sw_if_index,
Damjan Marionf56b77a2016-10-03 19:44:57 +0200212 last_info[payload_info.src])
213 last_info[payload_info.src] = next_info
214 self.assertTrue(next_info is not None)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200215 self.assertEqual(packet_index, next_info.index)
216 saved_packet = next_info.data
Damjan Marionf56b77a2016-10-03 19:44:57 +0200217 # Check standard fields
Klement Sekeraf62ae122016-10-11 11:47:09 +0200218 self.assertEqual(ip.src, saved_packet[IP].src)
219 self.assertEqual(ip.dst, saved_packet[IP].dst)
220 self.assertEqual(udp.sport, saved_packet[UDP].sport)
221 self.assertEqual(udp.dport, saved_packet[UDP].dport)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200222 except:
Klement Sekera7bb873a2016-11-18 07:38:42 +0100223 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200224 raise
Klement Sekeraf62ae122016-10-11 11:47:09 +0200225 for i in self.pg_interfaces:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200226 remaining_packet = self.get_next_packet_info_for_interface2(
Klement Sekeraf62ae122016-10-11 11:47:09 +0200227 i, dst_sw_if_index, last_info[i.sw_if_index])
228 self.assertTrue(
229 remaining_packet is None,
230 "Port %u: Packet expected from source %u didn't arrive" %
231 (dst_sw_if_index, i.sw_if_index))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200232
Jan49c0fca2016-10-26 15:44:27 +0200233 def run_l2bd_test(self, pkts_per_burst):
234 """ L2BD MAC learning test """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200235
Klement Sekeraf62ae122016-10-11 11:47:09 +0200236 # Create incoming packet streams for packet-generator interfaces
237 for i in self.pg_interfaces:
238 packet_sizes = self.sub_if_packet_sizes if hasattr(i, 'sub_if') \
239 else self.pg_if_packet_sizes
Jan49c0fca2016-10-26 15:44:27 +0200240 pkts = self.create_stream(i, packet_sizes, pkts_per_burst)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200241 i.add_stream(pkts)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200242
Klement Sekeraf62ae122016-10-11 11:47:09 +0200243 # Enable packet capture and start packet sending
244 self.pg_enable_capture(self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200245 self.pg_start()
246
Klement Sekeraf62ae122016-10-11 11:47:09 +0200247 # Verify outgoing packet streams per packet-generator interface
248 for i in self.pg_interfaces:
249 capture = i.get_capture()
Jan49c0fca2016-10-26 15:44:27 +0200250 self.logger.info("Verifying capture on interface %s" % i.name)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200251 self.verify_capture(i, capture)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200252
Jan49c0fca2016-10-26 15:44:27 +0200253 def test_l2bd_sl(self):
254 """ L2BD MAC learning single-loop test
255
256 Test scenario:
257 1.config
258 MAC learning enabled
Paul Vinciguerra8feeaff2019-03-27 11:25:48 -0700259 learn 100 MAC entries
Jan49c0fca2016-10-26 15:44:27 +0200260 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of
261 dot1ad in the first version)
262
263 2.sending l2 eth pkts between 3 interface
264 64B, 512B, 1518B, 9200B (ether_size)
265 burst of 2 pkts per interface
266 """
267
268 self.run_l2bd_test(self.sl_pkts_per_burst)
269
270 def test_l2bd_dl(self):
271 """ L2BD MAC learning dual-loop test
272
273 Test scenario:
274 1.config
275 MAC learning enabled
Paul Vinciguerra8feeaff2019-03-27 11:25:48 -0700276 learn 100 MAC entries
Jan49c0fca2016-10-26 15:44:27 +0200277 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of
278 dot1ad in the first version)
279
280 2.sending l2 eth pkts between 3 interface
281 64B, 512B, 1518B, 9200B (ether_size)
282 burst of 257 pkts per interface
283 """
284
285 self.run_l2bd_test(self.dl_pkts_per_burst)
286
Damjan Marionf56b77a2016-10-03 19:44:57 +0200287
288if __name__ == '__main__':
Klement Sekeraf62ae122016-10-11 11:47:09 +0200289 unittest.main(testRunner=VppTestRunner)