blob: 50720e64cd8dd25e44b9aa86e3e840b8f02547a0 [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()
Jan49c0fca2016-10-26 15:44:27 +0200102 self.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])
161 pkt_info = self.create_packet_info(
162 src_if.sw_if_index, dst_if.sw_if_index)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200163 payload = self.info_to_payload(pkt_info)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200164 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
165 IP(src=src_host.ip4, dst=dst_host.ip4) /
Damjan Marionf56b77a2016-10-03 19:44:57 +0200166 UDP(sport=1234, dport=1234) /
167 Raw(payload))
168 pkt_info.data = p.copy()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200169 if hasattr(src_if, 'sub_if'):
170 p = src_if.sub_if.add_dot1_layer(p)
Jan49c0fca2016-10-26 15:44:27 +0200171 size = random.choice(packet_sizes)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200172 self.extend_packet(p, size)
173 pkts.append(p)
174 return pkts
Damjan Marionf56b77a2016-10-03 19:44:57 +0200175
Klement Sekeraf62ae122016-10-11 11:47:09 +0200176 def verify_capture(self, pg_if, capture):
Jan49c0fca2016-10-26 15:44:27 +0200177 """
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 """
Klement Sekeraf62ae122016-10-11 11:47:09 +0200183 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
Damjan Marionf56b77a2016-10-03 19:44:57 +0200187 for packet in capture:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200188 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)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200200 try:
201 ip = packet[IP]
202 udp = packet[UDP]
Klement Sekeraf62ae122016-10-11 11:47:09 +0200203 packet_index = payload_info.index
204 self.assertEqual(payload_info.dst, dst_sw_if_index)
Jan49c0fca2016-10-26 15:44:27 +0200205 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
206 (pg_if.name, payload_info.src, packet_index))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200207 next_info = self.get_next_packet_info_for_interface2(
Klement Sekeraf62ae122016-10-11 11:47:09 +0200208 payload_info.src, dst_sw_if_index,
Damjan Marionf56b77a2016-10-03 19:44:57 +0200209 last_info[payload_info.src])
210 last_info[payload_info.src] = next_info
211 self.assertTrue(next_info is not None)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200212 self.assertEqual(packet_index, next_info.index)
213 saved_packet = next_info.data
Damjan Marionf56b77a2016-10-03 19:44:57 +0200214 # Check standard fields
Klement Sekeraf62ae122016-10-11 11:47:09 +0200215 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)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200219 except:
Klement Sekera7bb873a2016-11-18 07:38:42 +0100220 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200221 raise
Klement Sekeraf62ae122016-10-11 11:47:09 +0200222 for i in self.pg_interfaces:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200223 remaining_packet = self.get_next_packet_info_for_interface2(
Klement Sekeraf62ae122016-10-11 11:47:09 +0200224 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))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200229
Jan49c0fca2016-10-26 15:44:27 +0200230 def run_l2bd_test(self, pkts_per_burst):
231 """ L2BD MAC learning test """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200232
Klement Sekeraf62ae122016-10-11 11:47:09 +0200233 # 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
Jan49c0fca2016-10-26 15:44:27 +0200237 pkts = self.create_stream(i, packet_sizes, pkts_per_burst)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200238 i.add_stream(pkts)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200239
Klement Sekeraf62ae122016-10-11 11:47:09 +0200240 # Enable packet capture and start packet sending
241 self.pg_enable_capture(self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200242 self.pg_start()
243
Klement Sekeraf62ae122016-10-11 11:47:09 +0200244 # Verify outgoing packet streams per packet-generator interface
245 for i in self.pg_interfaces:
246 capture = i.get_capture()
Jan49c0fca2016-10-26 15:44:27 +0200247 self.logger.info("Verifying capture on interface %s" % i.name)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200248 self.verify_capture(i, capture)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200249
Jan49c0fca2016-10-26 15:44:27 +0200250 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
Damjan Marionf56b77a2016-10-03 19:44:57 +0200284
285if __name__ == '__main__':
Klement Sekeraf62ae122016-10-11 11:47:09 +0200286 unittest.main(testRunner=VppTestRunner)