blob: 3ce71e3d518d87283b933280847b78c13ba3aca5 [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
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
Dave Wallace8800f732023-08-31 00:47:44 -040010from framework import VppTestCase
11from asfframework import VppTestRunner
Klement Sekera7bb873a2016-11-18 07:38:42 +010012from util import Host, ppp
Jan49c0fca2016-10-26 15:44:27 +020013from vpp_sub_interface import VppDot1QSubint, VppDot1ADSubint
Damjan Marionf56b77a2016-10-03 19:44:57 +020014
15
Damjan Marionf56b77a2016-10-03 19:44:57 +020016class TestL2bd(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020017 """L2BD Test Case"""
Damjan Marionf56b77a2016-10-03 19:44:57 +020018
Damjan Marionf56b77a2016-10-03 19:44:57 +020019 @classmethod
20 def setUpClass(cls):
Jan49c0fca2016-10-26 15:44:27 +020021 """
22 Perform standard class setup (defined by class method setUpClass in
23 class VppTestCase) before running the test case, set test case related
24 variables and configure VPP.
25
26 :var int bd_id: Bridge domain ID.
27 :var int mac_entries_count: Number of MAC entries for bridge-domain to
28 learn.
29 :var int dot1q_tag: VLAN tag for dot1q sub-interface.
30 :var int dot1ad_sub_id: SubID of dot1ad sub-interface.
31 :var int dot1ad_outer_tag: VLAN S-tag for dot1ad sub-interface.
32 :var int dot1ad_inner_tag: VLAN C-tag for dot1ad sub-interface.
33 :var int sl_pkts_per_burst: Number of packets in burst for single-loop
34 test.
35 :var int dl_pkts_per_burst: Number of packets in burst for dual-loop
36 test.
37 """
Damjan Marionf56b77a2016-10-03 19:44:57 +020038 super(TestL2bd, cls).setUpClass()
39
Jan49c0fca2016-10-26 15:44:27 +020040 # Test variables
41 cls.bd_id = 1
42 cls.mac_entries_count = 100
43 # cls.dot1q_sub_id = 100
44 cls.dot1q_tag = 100
45 cls.dot1ad_sub_id = 20
46 cls.dot1ad_outer_tag = 200
47 cls.dot1ad_inner_tag = 300
48 cls.sl_pkts_per_burst = 2
49 cls.dl_pkts_per_burst = 257
50
51 try:
52 # create 3 pg interfaces
53 cls.create_pg_interfaces(range(3))
54
55 # create 2 sub-interfaces for pg1 and pg2
56 cls.sub_interfaces = [
57 VppDot1QSubint(cls, cls.pg1, cls.dot1q_tag),
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020058 VppDot1ADSubint(
59 cls,
60 cls.pg2,
61 cls.dot1ad_sub_id,
62 cls.dot1ad_outer_tag,
63 cls.dot1ad_inner_tag,
64 ),
65 ]
Jan49c0fca2016-10-26 15:44:27 +020066
67 # packet flows mapping pg0 -> pg1, pg2, etc.
68 cls.flows = dict()
69 cls.flows[cls.pg0] = [cls.pg1, cls.pg2]
70 cls.flows[cls.pg1] = [cls.pg0, cls.pg2]
71 cls.flows[cls.pg2] = [cls.pg0, cls.pg1]
72
73 # packet sizes
74 cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
75 cls.sub_if_packet_sizes = [64, 512, 1518 + 4, 9018 + 4]
76
77 cls.interfaces = list(cls.pg_interfaces)
78 cls.interfaces.extend(cls.sub_interfaces)
79
80 # Create BD with MAC learning enabled and put interfaces and
81 # sub-interfaces to this BD
82 for pg_if in cls.pg_interfaces:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020083 sw_if_index = (
84 pg_if.sub_if.sw_if_index
85 if hasattr(pg_if, "sub_if")
86 else pg_if.sw_if_index
87 )
88 cls.vapi.sw_interface_set_l2_bridge(
89 rx_sw_if_index=sw_if_index, bd_id=cls.bd_id
90 )
Jan49c0fca2016-10-26 15:44:27 +020091
92 # setup all interfaces
93 for i in cls.interfaces:
94 i.admin_up()
95
96 # mapping between packet-generator index and lists of test hosts
97 cls.hosts_by_pg_idx = dict()
98
99 # create test host entries and inject packets to learn MAC entries
100 # in the bridge-domain
101 cls.create_hosts_and_learn(cls.mac_entries_count)
102 cls.logger.info(cls.vapi.ppcli("show l2fib"))
103
104 except Exception:
105 super(TestL2bd, cls).tearDownClass()
106 raise
107
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700108 @classmethod
109 def tearDownClass(cls):
110 super(TestL2bd, cls).tearDownClass()
111
Klement Sekeraf62ae122016-10-11 11:47:09 +0200112 def setUp(self):
Jan49c0fca2016-10-26 15:44:27 +0200113 """
114 Clear trace and packet infos before running each test.
115 """
Klement Sekeraf62ae122016-10-11 11:47:09 +0200116 super(TestL2bd, self).setUp()
Klement Sekeradab231a2016-12-21 08:50:14 +0100117 self.reset_packet_infos()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200118
Damjan Marionf56b77a2016-10-03 19:44:57 +0200119 def tearDown(self):
Jan49c0fca2016-10-26 15:44:27 +0200120 """
121 Show various debug prints after each test.
122 """
Klement Sekeraf62ae122016-10-11 11:47:09 +0200123 super(TestL2bd, self).tearDown()
124 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200125 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200126 self.logger.info(
127 self.vapi.ppcli("show bridge-domain %s detail" % self.bd_id)
128 )
Damjan Marionf56b77a2016-10-03 19:44:57 +0200129
Jan49c0fca2016-10-26 15:44:27 +0200130 @classmethod
131 def create_hosts_and_learn(cls, count):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200132 """
133 Create required number of host MAC addresses and distribute them among
134 interfaces. Create host IPv4 address for every host MAC address. Create
135 L2 MAC packet stream with host MAC addresses per interface to let
136 the bridge domain learn these MAC addresses.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200137
Klement Sekeraf62ae122016-10-11 11:47:09 +0200138 :param count: Integer number of hosts to create MAC/IPv4 addresses for.
139 """
Jan49c0fca2016-10-26 15:44:27 +0200140 n_int = len(cls.pg_interfaces)
Ole Troan3f2a9562019-10-21 20:37:30 +0200141 macs_per_if = count // n_int
Klement Sekeraf62ae122016-10-11 11:47:09 +0200142 i = -1
Jan49c0fca2016-10-26 15:44:27 +0200143 for pg_if in cls.pg_interfaces:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200144 i += 1
145 start_nr = macs_per_if * i
146 end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
Jan49c0fca2016-10-26 15:44:27 +0200147 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
148 hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200149 packets = []
150 for j in range(start_nr, end_nr):
Matej Klotton0178d522016-11-04 11:11:44 +0100151 host = Host(
Klement Sekeraf62ae122016-10-11 11:47:09 +0200152 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200153 "172.17.1%02x.%u" % (pg_if.sw_if_index, j),
154 )
155 packet = Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200156 hosts.append(host)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200157 if hasattr(pg_if, "sub_if"):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200158 packet = pg_if.sub_if.add_dot1_layer(packet)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200159 packets.append(packet)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200160 pg_if.add_stream(packets)
Jan49c0fca2016-10-26 15:44:27 +0200161 cls.logger.info("Sending broadcast eth frames for MAC learning")
162 cls.pg_start()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200163
Jan49c0fca2016-10-26 15:44:27 +0200164 def create_stream(self, src_if, packet_sizes, packets_per_burst):
165 """
166 Create input packet stream for defined interface.
167
168 :param object src_if: Interface to create packet stream for.
169 :param list packet_sizes: List of required packet sizes.
170 :param int packets_per_burst: Number of packets in burst.
171 :return: Stream of packets.
172 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200173 pkts = []
Jan49c0fca2016-10-26 15:44:27 +0200174 for i in range(0, packets_per_burst):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200175 dst_if = self.flows[src_if][i % 2]
176 dst_host = random.choice(self.hosts_by_pg_idx[dst_if.sw_if_index])
177 src_host = random.choice(self.hosts_by_pg_idx[src_if.sw_if_index])
Klement Sekeradab231a2016-12-21 08:50:14 +0100178 pkt_info = self.create_packet_info(src_if, dst_if)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200179 payload = self.info_to_payload(pkt_info)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200180 p = (
181 Ether(dst=dst_host.mac, src=src_host.mac)
182 / IP(src=src_host.ip4, dst=dst_host.ip4)
183 / UDP(sport=1234, dport=1234)
184 / Raw(payload)
185 )
Damjan Marionf56b77a2016-10-03 19:44:57 +0200186 pkt_info.data = p.copy()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200187 if hasattr(src_if, "sub_if"):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200188 p = src_if.sub_if.add_dot1_layer(p)
Jan49c0fca2016-10-26 15:44:27 +0200189 size = random.choice(packet_sizes)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200190 self.extend_packet(p, size)
191 pkts.append(p)
192 return pkts
Damjan Marionf56b77a2016-10-03 19:44:57 +0200193
Klement Sekeraf62ae122016-10-11 11:47:09 +0200194 def verify_capture(self, pg_if, capture):
Jan49c0fca2016-10-26 15:44:27 +0200195 """
196 Verify captured input packet stream for defined interface.
197
198 :param object pg_if: Interface to verify captured packet stream for.
199 :param list capture: Captured packet stream.
200 """
Klement Sekeraf62ae122016-10-11 11:47:09 +0200201 last_info = dict()
202 for i in self.pg_interfaces:
203 last_info[i.sw_if_index] = None
204 dst_sw_if_index = pg_if.sw_if_index
Damjan Marionf56b77a2016-10-03 19:44:57 +0200205 for packet in capture:
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800206 payload_info = self.payload_to_info(packet[Raw])
Klement Sekeraf62ae122016-10-11 11:47:09 +0200207 src_sw_if_index = payload_info.src
208 src_if = None
209 for ifc in self.pg_interfaces:
210 if ifc != pg_if:
211 if ifc.sw_if_index == src_sw_if_index:
212 src_if = ifc
213 break
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200214 if hasattr(src_if, "sub_if"):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200215 # Check VLAN tags and Ethernet header
216 packet = src_if.sub_if.remove_dot1_layer(packet)
217 self.assertTrue(Dot1Q not in packet)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200218 try:
219 ip = packet[IP]
220 udp = packet[UDP]
Klement Sekeraf62ae122016-10-11 11:47:09 +0200221 packet_index = payload_info.index
222 self.assertEqual(payload_info.dst, dst_sw_if_index)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200223 self.logger.debug(
224 "Got packet on port %s: src=%u (id=%u)"
225 % (pg_if.name, payload_info.src, packet_index)
226 )
Damjan Marionf56b77a2016-10-03 19:44:57 +0200227 next_info = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200228 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
229 )
Damjan Marionf56b77a2016-10-03 19:44:57 +0200230 last_info[payload_info.src] = next_info
231 self.assertTrue(next_info is not None)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200232 self.assertEqual(packet_index, next_info.index)
233 saved_packet = next_info.data
Damjan Marionf56b77a2016-10-03 19:44:57 +0200234 # Check standard fields
Klement Sekeraf62ae122016-10-11 11:47:09 +0200235 self.assertEqual(ip.src, saved_packet[IP].src)
236 self.assertEqual(ip.dst, saved_packet[IP].dst)
237 self.assertEqual(udp.sport, saved_packet[UDP].sport)
238 self.assertEqual(udp.dport, saved_packet[UDP].dport)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200239 except:
Klement Sekera7bb873a2016-11-18 07:38:42 +0100240 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200241 raise
Klement Sekeraf62ae122016-10-11 11:47:09 +0200242 for i in self.pg_interfaces:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200243 remaining_packet = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200244 i, dst_sw_if_index, last_info[i.sw_if_index]
245 )
Klement Sekeraf62ae122016-10-11 11:47:09 +0200246 self.assertTrue(
247 remaining_packet is None,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200248 "Port %u: Packet expected from source %u didn't arrive"
249 % (dst_sw_if_index, i.sw_if_index),
250 )
Damjan Marionf56b77a2016-10-03 19:44:57 +0200251
Jan49c0fca2016-10-26 15:44:27 +0200252 def run_l2bd_test(self, pkts_per_burst):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200253 """L2BD MAC learning test"""
Damjan Marionf56b77a2016-10-03 19:44:57 +0200254
Klement Sekeraf62ae122016-10-11 11:47:09 +0200255 # Create incoming packet streams for packet-generator interfaces
256 for i in self.pg_interfaces:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200257 packet_sizes = (
258 self.sub_if_packet_sizes
259 if hasattr(i, "sub_if")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200260 else self.pg_if_packet_sizes
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200261 )
Jan49c0fca2016-10-26 15:44:27 +0200262 pkts = self.create_stream(i, packet_sizes, pkts_per_burst)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200263 i.add_stream(pkts)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200264
Klement Sekeraf62ae122016-10-11 11:47:09 +0200265 # Enable packet capture and start packet sending
266 self.pg_enable_capture(self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200267 self.pg_start()
268
Klement Sekeraf62ae122016-10-11 11:47:09 +0200269 # Verify outgoing packet streams per packet-generator interface
270 for i in self.pg_interfaces:
271 capture = i.get_capture()
Jan49c0fca2016-10-26 15:44:27 +0200272 self.logger.info("Verifying capture on interface %s" % i.name)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200273 self.verify_capture(i, capture)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200274
Jan49c0fca2016-10-26 15:44:27 +0200275 def test_l2bd_sl(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200276 """L2BD MAC learning single-loop test
Jan49c0fca2016-10-26 15:44:27 +0200277
278 Test scenario:
279 1.config
280 MAC learning enabled
Paul Vinciguerra8feeaff2019-03-27 11:25:48 -0700281 learn 100 MAC entries
Jan49c0fca2016-10-26 15:44:27 +0200282 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of
283 dot1ad in the first version)
284
285 2.sending l2 eth pkts between 3 interface
286 64B, 512B, 1518B, 9200B (ether_size)
287 burst of 2 pkts per interface
288 """
289
290 self.run_l2bd_test(self.sl_pkts_per_burst)
291
292 def test_l2bd_dl(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200293 """L2BD MAC learning dual-loop test
Jan49c0fca2016-10-26 15:44:27 +0200294
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200295 Test scenario:
296 1.config
297 MAC learning enabled
298 learn 100 MAC entries
299 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of
300 dot1ad in the first version)
Jan49c0fca2016-10-26 15:44:27 +0200301
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200302 2.sending l2 eth pkts between 3 interface
303 64B, 512B, 1518B, 9200B (ether_size)
304 burst of 257 pkts per interface
Jan49c0fca2016-10-26 15:44:27 +0200305 """
306
307 self.run_l2bd_test(self.dl_pkts_per_burst)
308
Damjan Marionf56b77a2016-10-03 19:44:57 +0200309
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200310if __name__ == "__main__":
Klement Sekeraf62ae122016-10-11 11:47:09 +0200311 unittest.main(testRunner=VppTestRunner)