6 from scapy.packet import Raw
7 from scapy.layers.l2 import Ether, Dot1Q
8 from scapy.layers.inet import IP, UDP
10 from framework import VppTestCase
11 from asfframework import VppTestRunner
12 from util import Host, ppp
13 from vpp_sub_interface import VppDot1QSubint, VppDot1ADSubint
16 class TestL2bd(VppTestCase):
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.
26 :var int bd_id: Bridge domain ID.
27 :var int mac_entries_count: Number of MAC entries for bridge-domain to
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
35 :var int dl_pkts_per_burst: Number of packets in burst for dual-loop
38 super(TestL2bd, cls).setUpClass()
42 cls.mac_entries_count = 100
43 # cls.dot1q_sub_id = 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
52 # create 3 pg interfaces
53 cls.create_pg_interfaces(range(3))
55 # create 2 sub-interfaces for pg1 and pg2
56 cls.sub_interfaces = [
57 VppDot1QSubint(cls, cls.pg1, cls.dot1q_tag),
67 # packet flows mapping pg0 -> pg1, pg2, etc.
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]
74 cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
75 cls.sub_if_packet_sizes = [64, 512, 1518 + 4, 9018 + 4]
77 cls.interfaces = list(cls.pg_interfaces)
78 cls.interfaces.extend(cls.sub_interfaces)
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:
84 pg_if.sub_if.sw_if_index
85 if hasattr(pg_if, "sub_if")
86 else pg_if.sw_if_index
88 cls.vapi.sw_interface_set_l2_bridge(
89 rx_sw_if_index=sw_if_index, bd_id=cls.bd_id
92 # setup all interfaces
93 for i in cls.interfaces:
96 # mapping between packet-generator index and lists of test hosts
97 cls.hosts_by_pg_idx = dict()
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"))
105 super(TestL2bd, cls).tearDownClass()
109 def tearDownClass(cls):
110 super(TestL2bd, cls).tearDownClass()
114 Clear trace and packet infos before running each test.
116 super(TestL2bd, self).setUp()
117 self.reset_packet_infos()
121 Show various debug prints after each test.
123 super(TestL2bd, self).tearDown()
124 if not self.vpp_dead:
125 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
127 self.vapi.ppcli("show bridge-domain %s detail" % self.bd_id)
131 def create_hosts_and_learn(cls, count):
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.
138 :param count: Integer number of hosts to create MAC/IPv4 addresses for.
140 n_int = len(cls.pg_interfaces)
141 macs_per_if = count // n_int
143 for pg_if in cls.pg_interfaces:
145 start_nr = macs_per_if * i
146 end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
147 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
148 hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
150 for j in range(start_nr, end_nr):
152 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
153 "172.17.1%02x.%u" % (pg_if.sw_if_index, j),
155 packet = Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
157 if hasattr(pg_if, "sub_if"):
158 packet = pg_if.sub_if.add_dot1_layer(packet)
159 packets.append(packet)
160 pg_if.add_stream(packets)
161 cls.logger.info("Sending broadcast eth frames for MAC learning")
164 def create_stream(self, src_if, packet_sizes, packets_per_burst):
166 Create input packet stream for defined interface.
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.
174 for i in range(0, packets_per_burst):
175 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])
178 pkt_info = self.create_packet_info(src_if, dst_if)
179 payload = self.info_to_payload(pkt_info)
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)
186 pkt_info.data = p.copy()
187 if hasattr(src_if, "sub_if"):
188 p = src_if.sub_if.add_dot1_layer(p)
189 size = random.choice(packet_sizes)
190 self.extend_packet(p, size)
194 def verify_capture(self, pg_if, capture):
196 Verify captured input packet stream for defined interface.
198 :param object pg_if: Interface to verify captured packet stream for.
199 :param list capture: Captured packet stream.
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
205 for packet in capture:
206 payload_info = self.payload_to_info(packet[Raw])
207 src_sw_if_index = payload_info.src
209 for ifc in self.pg_interfaces:
211 if ifc.sw_if_index == src_sw_if_index:
214 if hasattr(src_if, "sub_if"):
215 # Check VLAN tags and Ethernet header
216 packet = src_if.sub_if.remove_dot1_layer(packet)
217 self.assertTrue(Dot1Q not in packet)
221 packet_index = payload_info.index
222 self.assertEqual(payload_info.dst, dst_sw_if_index)
224 "Got packet on port %s: src=%u (id=%u)"
225 % (pg_if.name, payload_info.src, packet_index)
227 next_info = self.get_next_packet_info_for_interface2(
228 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
230 last_info[payload_info.src] = next_info
231 self.assertTrue(next_info is not None)
232 self.assertEqual(packet_index, next_info.index)
233 saved_packet = next_info.data
234 # Check standard fields
235 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)
240 self.logger.error(ppp("Unexpected or invalid packet:", packet))
242 for i in self.pg_interfaces:
243 remaining_packet = self.get_next_packet_info_for_interface2(
244 i, dst_sw_if_index, last_info[i.sw_if_index]
247 remaining_packet is None,
248 "Port %u: Packet expected from source %u didn't arrive"
249 % (dst_sw_if_index, i.sw_if_index),
252 def run_l2bd_test(self, pkts_per_burst):
253 """L2BD MAC learning test"""
255 # Create incoming packet streams for packet-generator interfaces
256 for i in self.pg_interfaces:
258 self.sub_if_packet_sizes
259 if hasattr(i, "sub_if")
260 else self.pg_if_packet_sizes
262 pkts = self.create_stream(i, packet_sizes, pkts_per_burst)
265 # Enable packet capture and start packet sending
266 self.pg_enable_capture(self.pg_interfaces)
269 # Verify outgoing packet streams per packet-generator interface
270 for i in self.pg_interfaces:
271 capture = i.get_capture()
272 self.logger.info("Verifying capture on interface %s" % i.name)
273 self.verify_capture(i, capture)
275 def test_l2bd_sl(self):
276 """L2BD MAC learning single-loop test
281 learn 100 MAC entries
282 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of
283 dot1ad in the first version)
285 2.sending l2 eth pkts between 3 interface
286 64B, 512B, 1518B, 9200B (ether_size)
287 burst of 2 pkts per interface
290 self.run_l2bd_test(self.sl_pkts_per_burst)
292 def test_l2bd_dl(self):
293 """L2BD MAC learning dual-loop test
298 learn 100 MAC entries
299 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of
300 dot1ad in the first version)
302 2.sending l2 eth pkts between 3 interface
303 64B, 512B, 1518B, 9200B (ether_size)
304 burst of 257 pkts per interface
307 self.run_l2bd_test(self.dl_pkts_per_burst)
310 if __name__ == "__main__":
311 unittest.main(testRunner=VppTestRunner)