blob: 30708a46a03a7d02e498758524fc5734a743837e [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
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
Klement Sekeraf62ae122016-10-11 11:47:09 +020097 def setUp(self):
Jan49c0fca2016-10-26 15:44:27 +020098 """
99 Clear trace and packet infos before running each test.
100 """
Klement Sekeraf62ae122016-10-11 11:47:09 +0200101 super(TestL2bd, self).setUp()
Klement Sekeradab231a2016-12-21 08:50:14 +0100102 self.reset_packet_infos()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200103
Damjan Marionf56b77a2016-10-03 19:44:57 +0200104 def tearDown(self):
Jan49c0fca2016-10-26 15:44:27 +0200105 """
106 Show various debug prints after each test.
107 """
Klement Sekeraf62ae122016-10-11 11:47:09 +0200108 super(TestL2bd, self).tearDown()
109 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200110 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
111 self.logger.info(self.vapi.ppcli("show bridge-domain %s detail" %
Klement Sekera7bb873a2016-11-18 07:38:42 +0100112 self.bd_id))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200113
Jan49c0fca2016-10-26 15:44:27 +0200114 @classmethod
115 def create_hosts_and_learn(cls, count):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200116 """
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.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200121
Klement Sekeraf62ae122016-10-11 11:47:09 +0200122 :param count: Integer number of hosts to create MAC/IPv4 addresses for.
123 """
Jan49c0fca2016-10-26 15:44:27 +0200124 n_int = len(cls.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200125 macs_per_if = count / n_int
Klement Sekeraf62ae122016-10-11 11:47:09 +0200126 i = -1
Jan49c0fca2016-10-26 15:44:27 +0200127 for pg_if in cls.pg_interfaces:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200128 i += 1
129 start_nr = macs_per_if * i
130 end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
Jan49c0fca2016-10-26 15:44:27 +0200131 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
132 hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200133 packets = []
134 for j in range(start_nr, end_nr):
Matej Klotton0178d522016-11-04 11:11:44 +0100135 host = Host(
Klement Sekeraf62ae122016-10-11 11:47:09 +0200136 "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)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200142 packets.append(packet)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200143 pg_if.add_stream(packets)
Jan49c0fca2016-10-26 15:44:27 +0200144 cls.logger.info("Sending broadcast eth frames for MAC learning")
145 cls.pg_start()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200146
Jan49c0fca2016-10-26 15:44:27 +0200147 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 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200156 pkts = []
Jan49c0fca2016-10-26 15:44:27 +0200157 for i in range(0, packets_per_burst):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200158 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])
Klement Sekeradab231a2016-12-21 08:50:14 +0100161 pkt_info = self.create_packet_info(src_if, dst_if)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200162 payload = self.info_to_payload(pkt_info)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200163 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
164 IP(src=src_host.ip4, dst=dst_host.ip4) /
Damjan Marionf56b77a2016-10-03 19:44:57 +0200165 UDP(sport=1234, dport=1234) /
166 Raw(payload))
167 pkt_info.data = p.copy()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200168 if hasattr(src_if, 'sub_if'):
169 p = src_if.sub_if.add_dot1_layer(p)
Jan49c0fca2016-10-26 15:44:27 +0200170 size = random.choice(packet_sizes)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200171 self.extend_packet(p, size)
172 pkts.append(p)
173 return pkts
Damjan Marionf56b77a2016-10-03 19:44:57 +0200174
Klement Sekeraf62ae122016-10-11 11:47:09 +0200175 def verify_capture(self, pg_if, capture):
Jan49c0fca2016-10-26 15:44:27 +0200176 """
177 Verify captured input packet stream for defined interface.
178
179 :param object pg_if: Interface to verify captured packet stream for.
180 :param list capture: Captured packet stream.
181 """
Klement Sekeraf62ae122016-10-11 11:47:09 +0200182 last_info = dict()
183 for i in self.pg_interfaces:
184 last_info[i.sw_if_index] = None
185 dst_sw_if_index = pg_if.sw_if_index
Damjan Marionf56b77a2016-10-03 19:44:57 +0200186 for packet in capture:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200187 payload_info = self.payload_to_info(str(packet[Raw]))
188 src_sw_if_index = payload_info.src
189 src_if = None
190 for ifc in self.pg_interfaces:
191 if ifc != pg_if:
192 if ifc.sw_if_index == src_sw_if_index:
193 src_if = ifc
194 break
195 if hasattr(src_if, 'sub_if'):
196 # Check VLAN tags and Ethernet header
197 packet = src_if.sub_if.remove_dot1_layer(packet)
198 self.assertTrue(Dot1Q not in packet)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200199 try:
200 ip = packet[IP]
201 udp = packet[UDP]
Klement Sekeraf62ae122016-10-11 11:47:09 +0200202 packet_index = payload_info.index
203 self.assertEqual(payload_info.dst, dst_sw_if_index)
Jan49c0fca2016-10-26 15:44:27 +0200204 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
205 (pg_if.name, payload_info.src, packet_index))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200206 next_info = self.get_next_packet_info_for_interface2(
Klement Sekeraf62ae122016-10-11 11:47:09 +0200207 payload_info.src, dst_sw_if_index,
Damjan Marionf56b77a2016-10-03 19:44:57 +0200208 last_info[payload_info.src])
209 last_info[payload_info.src] = next_info
210 self.assertTrue(next_info is not None)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200211 self.assertEqual(packet_index, next_info.index)
212 saved_packet = next_info.data
Damjan Marionf56b77a2016-10-03 19:44:57 +0200213 # Check standard fields
Klement Sekeraf62ae122016-10-11 11:47:09 +0200214 self.assertEqual(ip.src, saved_packet[IP].src)
215 self.assertEqual(ip.dst, saved_packet[IP].dst)
216 self.assertEqual(udp.sport, saved_packet[UDP].sport)
217 self.assertEqual(udp.dport, saved_packet[UDP].dport)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200218 except:
Klement Sekera7bb873a2016-11-18 07:38:42 +0100219 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200220 raise
Klement Sekeraf62ae122016-10-11 11:47:09 +0200221 for i in self.pg_interfaces:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200222 remaining_packet = self.get_next_packet_info_for_interface2(
Klement Sekeraf62ae122016-10-11 11:47:09 +0200223 i, dst_sw_if_index, last_info[i.sw_if_index])
224 self.assertTrue(
225 remaining_packet is None,
226 "Port %u: Packet expected from source %u didn't arrive" %
227 (dst_sw_if_index, i.sw_if_index))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200228
Jan49c0fca2016-10-26 15:44:27 +0200229 def run_l2bd_test(self, pkts_per_burst):
230 """ L2BD MAC learning test """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200231
Klement Sekeraf62ae122016-10-11 11:47:09 +0200232 # Create incoming packet streams for packet-generator interfaces
233 for i in self.pg_interfaces:
234 packet_sizes = self.sub_if_packet_sizes if hasattr(i, 'sub_if') \
235 else self.pg_if_packet_sizes
Jan49c0fca2016-10-26 15:44:27 +0200236 pkts = self.create_stream(i, packet_sizes, pkts_per_burst)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200237 i.add_stream(pkts)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200238
Klement Sekeraf62ae122016-10-11 11:47:09 +0200239 # Enable packet capture and start packet sending
240 self.pg_enable_capture(self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200241 self.pg_start()
242
Klement Sekeraf62ae122016-10-11 11:47:09 +0200243 # Verify outgoing packet streams per packet-generator interface
244 for i in self.pg_interfaces:
245 capture = i.get_capture()
Jan49c0fca2016-10-26 15:44:27 +0200246 self.logger.info("Verifying capture on interface %s" % i.name)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200247 self.verify_capture(i, capture)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200248
Jan49c0fca2016-10-26 15:44:27 +0200249 def test_l2bd_sl(self):
250 """ L2BD MAC learning single-loop test
251
252 Test scenario:
253 1.config
254 MAC learning enabled
255 learn 100 MAC enries
256 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of
257 dot1ad in the first version)
258
259 2.sending l2 eth pkts between 3 interface
260 64B, 512B, 1518B, 9200B (ether_size)
261 burst of 2 pkts per interface
262 """
263
264 self.run_l2bd_test(self.sl_pkts_per_burst)
265
266 def test_l2bd_dl(self):
267 """ L2BD MAC learning dual-loop test
268
269 Test scenario:
270 1.config
271 MAC learning enabled
272 learn 100 MAC enries
273 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of
274 dot1ad in the first version)
275
276 2.sending l2 eth pkts between 3 interface
277 64B, 512B, 1518B, 9200B (ether_size)
278 burst of 257 pkts per interface
279 """
280
281 self.run_l2bd_test(self.dl_pkts_per_burst)
282
Damjan Marionf56b77a2016-10-03 19:44:57 +0200283
284if __name__ == '__main__':
Klement Sekeraf62ae122016-10-11 11:47:09 +0200285 unittest.main(testRunner=VppTestRunner)