5 from scapy.packet import Raw
6 from scapy.layers.l2 import Ether, Dot1Q, GRE
7 from scapy.layers.inet import IP, UDP
8 from scapy.layers.vxlan import VXLAN
10 from framework import VppTestCase, VppTestRunner
11 from util import Host, ppp
12 from vpp_sub_interface import VppDot1QSubint, VppDot1ADSubint
13 from vpp_gre_interface import VppGreInterface, VppGre6Interface
14 from vpp_papi_provider import L2_VTR_OP
15 from collections import namedtuple
17 Tag = namedtuple('Tag', ['dot1', 'vlan'])
22 class TestSpan(VppTestCase):
23 """ SPAN Test Case """
27 super(TestSpan, cls).setUpClass()
29 cls.hosts_nr = 10 # Number of hosts
30 cls.pkts_per_burst = 257 # Number of packets per burst
31 # create 3 pg interfaces
32 cls.create_pg_interfaces(range(3))
35 cls.sub_if = VppDot1QSubint(cls, cls.pg0, 100)
36 cls.dst_sub_if = VppDot1QSubint(cls, cls.pg2, 300)
37 cls.dst_sub_if.set_vtr(L2_VTR_OP.L2_POP_1, tag=300)
38 # packet flows mapping pg0 -> pg1, pg2 -> pg3, etc.
40 cls.flows[cls.pg0] = [cls.pg1]
43 cls.pg_if_packet_sizes = [64, 512] # , 1518, 9018]
45 cls.interfaces = list(cls.pg_interfaces)
47 # Create host MAC and IPv4 lists
48 # cls.MY_MACS = dict()
49 # cls.MY_IP4S = dict()
50 cls.create_host_lists(cls.hosts_nr)
52 # setup all interfaces
53 for i in cls.interfaces:
58 cls.vxlan = cls.vapi.vxlan_add_del_tunnel(
59 src_addr=cls.pg2.local_ip4n,
60 dst_addr=cls.pg2.remote_ip4n,
65 super(TestSpan, self).setUp()
66 self.reset_packet_infos()
69 super(TestSpan, self).tearDown()
71 self.logger.info(self.vapi.ppcli("show interface span"))
73 def xconnect(self, a, b, is_add=1):
74 self.vapi.sw_interface_set_l2_xconnect(a, b, enable=is_add)
75 self.vapi.sw_interface_set_l2_xconnect(b, a, enable=is_add)
77 def bridge(self, sw_if_index, is_add=1):
78 self.vapi.sw_interface_set_l2_bridge(
79 sw_if_index, bd_id=self.bd_id, enable=is_add)
81 def _remove_tag(self, packet, vlan, tag_type):
82 self.assertEqual(packet.type, tag_type)
83 payload = packet.payload
84 self.assertEqual(payload.vlan, vlan)
85 inner_type = payload.type
86 payload = payload.payload
87 packet.remove_payload()
88 packet.add_payload(payload)
89 packet.type = inner_type
91 def remove_tags(self, packet, tags):
93 self._remove_tag(packet, t.vlan, t.dot1)
96 def decap_gre(self, pkt):
98 Decapsulate the original payload frame by removing GRE header
100 self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
101 self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
103 self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
104 self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
106 return pkt[GRE].payload
108 def decap_vxlan(self, pkt):
110 Decapsulate the original payload frame by removing VXLAN header
112 self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
113 self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
115 self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
116 self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
118 return pkt[VXLAN].payload
121 def create_host_lists(self, count):
122 """ Method to create required number of MAC and IPv4 addresses.
123 Create required number of host MAC addresses and distribute them among
124 interfaces. Create host IPv4 address for every host MAC address too.
126 :param count: Number of hosts to create MAC and IPv4 addresses for.
128 # mapping between packet-generator index and lists of test hosts
129 self.hosts_by_pg_idx = dict()
131 for pg_if in self.pg_interfaces:
132 # self.MY_MACS[i.sw_if_index] = []
133 # self.MY_IP4S[i.sw_if_index] = []
134 self.hosts_by_pg_idx[pg_if.sw_if_index] = []
135 hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
136 for j in range(0, count):
138 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
139 "172.17.1%02x.%u" % (pg_if.sw_if_index, j))
142 def create_stream(self, src_if, packet_sizes, do_dot1=False):
144 for i in range(0, self.pkts_per_burst):
145 dst_if = self.flows[src_if][0]
146 pkt_info = self.create_packet_info(src_if, dst_if)
147 payload = self.info_to_payload(pkt_info)
148 p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
149 IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) /
150 UDP(sport=1234, dport=1234) /
153 p = self.sub_if.add_dot1_layer(p)
154 pkt_info.data = p.copy()
155 size = packet_sizes[(i / 2) % len(packet_sizes)]
156 self.extend_packet(p, size)
160 def verify_capture(self, dst_if, capture_pg1, capture_pg2):
162 for i in self.interfaces:
163 last_info[i.sw_if_index] = None
164 dst_sw_if_index = dst_if.sw_if_index
168 "Different number of outgoing and mirrored packets : %u != %u" %
171 for pkt_pg1, pkt_pg2 in zip(capture_pg1, capture_pg2):
177 if pkt_pg1[Ether] != pkt_pg2[Ether]:
178 self.logger.error("Different ethernet header of "
179 "outgoing and mirrored packet")
181 if ip1 != pkt_pg2[IP]:
183 "Different ip header of outgoing and mirrored packet")
185 if udp1 != pkt_pg2[UDP]:
187 "Different udp header of outgoing and mirrored packet")
189 if raw1 != pkt_pg2[Raw]:
191 "Different raw data of outgoing and mirrored packet")
194 payload_info = self.payload_to_info(str(raw1))
195 packet_index = payload_info.index
196 self.assertEqual(payload_info.dst, dst_sw_if_index)
198 "Got packet on port %s: src=%u (id=%u)" %
199 (dst_if.name, payload_info.src, packet_index))
200 next_info = self.get_next_packet_info_for_interface2(
201 payload_info.src, dst_sw_if_index,
202 last_info[payload_info.src])
203 last_info[payload_info.src] = next_info
204 self.assertTrue(next_info is not None)
205 self.assertEqual(packet_index, next_info.index)
206 saved_packet = next_info.data
207 # Check standard fields
208 self.assertEqual(ip1.src, saved_packet[IP].src)
209 self.assertEqual(ip1.dst, saved_packet[IP].dst)
210 self.assertEqual(udp1.sport, saved_packet[UDP].sport)
211 self.assertEqual(udp1.dport, saved_packet[UDP].dport)
213 self.logger.error("Unexpected or invalid packets:")
214 self.logger.error(ppp("pg1 packet:", pkt_pg1))
215 self.logger.error(ppp("pg2 packet:", pkt_pg2))
217 for i in self.interfaces:
218 remaining_packet = self.get_next_packet_info_for_interface2(
219 i, dst_sw_if_index, last_info[i.sw_if_index])
220 self.assertTrue(remaining_packet is None,
221 "Port %u: Packet expected from source %u didn't"
222 " arrive" % (dst_sw_if_index, i.sw_if_index))
224 def test_device_span(self):
225 """ SPAN device rx mirror test
229 3 interfaces, pg0 l2xconnected with pg1
230 2. sending l2 eth packets between 2 interfaces (pg0, pg1) and
232 64B, 512B, 1518B, 9018B (ether_size)
233 burst of packets per interface
236 # Create bi-directional cross-connects between pg0 and pg1
237 self.xconnect(self.pg0.sw_if_index, self.pg1.sw_if_index)
238 # Create incoming packet streams for packet-generator interfaces
239 pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes)
240 self.pg0.add_stream(pkts)
242 # Enable SPAN on pg0 (mirrored to pg2)
243 self.vapi.sw_interface_span_enable_disable(
244 self.pg0.sw_if_index, self.pg2.sw_if_index)
246 self.logger.info(self.vapi.ppcli("show interface span"))
247 # Enable packet capturing and start packet sending
248 self.pg_enable_capture(self.pg_interfaces)
251 # Verify packets outgoing packet streams on mirrored interface (pg2)
252 self.logger.info("Verifying capture on interfaces %s and %s" %
253 (self.pg1.name, self.pg2.name))
254 pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
257 self.pg1.get_capture(),
258 self.pg2.get_capture(pg2_expected))
260 # Disable SPAN on pg0 (mirrored to pg2)
261 self.vapi.sw_interface_span_enable_disable(
262 self.pg0.sw_if_index, self.pg2.sw_if_index, state=0)
263 self.xconnect(self.pg0.sw_if_index, self.pg1.sw_if_index, is_add=0)
265 def test_span_l2_rx(self):
266 """ SPAN l2 rx mirror test """
268 self.sub_if.admin_up()
270 self.bridge(self.pg2.sw_if_index)
271 # Create bi-directional cross-connects between pg0 and pg1
272 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
273 # Create incoming packet streams for packet-generator interfaces
274 pkts = self.create_stream(
275 self.pg0, self.pg_if_packet_sizes, do_dot1=True)
276 self.pg0.add_stream(pkts)
278 # Enable SPAN on pg0 (mirrored to pg2)
279 self.vapi.sw_interface_span_enable_disable(
280 self.sub_if.sw_if_index, self.pg2.sw_if_index, is_l2=1)
282 self.logger.info(self.vapi.ppcli("show interface span"))
283 # Enable packet capturing and start packet sending
284 self.pg_enable_capture(self.pg_interfaces)
287 # Verify packets outgoing packet streams on mirrored interface (pg2)
288 self.logger.info("Verifying capture on interfaces %s and %s" %
289 (self.pg1.name, self.pg2.name))
290 pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
291 pg1_pkts = self.pg1.get_capture()
292 pg2_pkts = self.pg2.get_capture(pg2_expected)
298 self.bridge(self.pg2.sw_if_index, is_add=0)
299 # Disable SPAN on pg0 (mirrored to pg2)
300 self.vapi.sw_interface_span_enable_disable(
301 self.sub_if.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1)
302 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
304 def test_span_l2_rx_dst_vxlan(self):
305 """ SPAN l2 rx mirror into vxlan test """
307 self.sub_if.admin_up()
308 self.vapi.sw_interface_set_flags(self.vxlan.sw_if_index,
311 self.bridge(self.vxlan.sw_if_index, is_add=1)
312 # Create bi-directional cross-connects between pg0 and pg1
313 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
314 # Create incoming packet streams for packet-generator interfaces
315 pkts = self.create_stream(
316 self.pg0, self.pg_if_packet_sizes, do_dot1=True)
317 self.pg0.add_stream(pkts)
319 # Enable SPAN on pg0 sub if (mirrored to vxlan)
320 self.vapi.sw_interface_span_enable_disable(
321 self.sub_if.sw_if_index, self.vxlan.sw_if_index, is_l2=1)
323 self.logger.info(self.vapi.ppcli("show interface span"))
324 # Enable packet capturing and start packet sending
325 self.pg_enable_capture(self.pg_interfaces)
328 # Verify packets outgoing packet streams on mirrored interface (pg2)
329 self.logger.info("Verifying capture on interfaces %s and %s" %
330 (self.pg1.name, self.pg2.name))
331 pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
332 pg1_pkts = self.pg1.get_capture()
333 pg2_pkts = [self.decap_vxlan(p)
334 for p in self.pg2.get_capture(pg2_expected)]
340 self.bridge(self.vxlan.sw_if_index, is_add=0)
341 # Disable SPAN on pg0 sub if (mirrored to vxlan)
342 self.vapi.sw_interface_span_enable_disable(
343 self.sub_if.sw_if_index, self.vxlan.sw_if_index, state=0, is_l2=1)
344 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
346 def test_span_l2_rx_dst_gre_subif_vtr(self):
347 """ SPAN l2 rx mirror into gre-subif+vtr """
349 self.sub_if.admin_up()
351 gre_if = VppGreInterface(self, self.pg2.local_ip4,
355 gre_if.add_vpp_config()
358 gre_sub_if = VppDot1QSubint(self, gre_if, 500)
359 gre_sub_if.set_vtr(L2_VTR_OP.L2_POP_1, tag=500)
360 gre_sub_if.admin_up()
362 self.bridge(gre_sub_if.sw_if_index)
363 # Create bi-directional cross-connects between pg0 and pg1
364 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
366 # Create incoming packet streams for packet-generator interfaces
367 pkts = self.create_stream(
368 self.pg0, self.pg_if_packet_sizes, do_dot1=True)
369 self.pg0.add_stream(pkts)
371 self.vapi.sw_interface_span_enable_disable(
372 self.sub_if.sw_if_index, gre_sub_if.sw_if_index, is_l2=1)
374 # Enable packet capturing and start packet sending
375 self.pg_enable_capture(self.pg_interfaces)
378 # Verify packets outgoing packet streams on mirrored interface (pg2)
379 self.logger.info("Verifying capture on interfaces %s and %s" %
380 (self.pg1.name, self.pg2.name))
381 pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
382 pg1_pkts = self.pg1.get_capture()
383 pg2_pkts = self.pg2.get_capture(pg2_expected)
384 pg2_decaped = [self.remove_tags(self.decap_gre(
385 p), [Tag(dot1=DOT1Q, vlan=500)]) for p in pg2_pkts]
391 self.bridge(gre_sub_if.sw_if_index, is_add=0)
392 # Disable SPAN on pg0 sub if
393 self.vapi.sw_interface_span_enable_disable(
394 self.sub_if.sw_if_index, gre_sub_if.sw_if_index, state=0,
396 gre_if.remove_vpp_config()
397 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
399 def test_span_l2_rx_dst_vtr(self):
400 """ SPAN l2 rx mirror into subif+vtr """
402 self.sub_if.admin_up()
403 self.dst_sub_if.admin_up()
405 self.bridge(self.dst_sub_if.sw_if_index)
406 # Create bi-directional cross-connects between pg0 and pg1
407 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
409 # Create incoming packet streams for packet-generator interfaces
410 pkts = self.create_stream(
411 self.pg0, self.pg_if_packet_sizes, do_dot1=True)
412 self.pg0.add_stream(pkts)
414 self.vapi.sw_interface_span_enable_disable(
415 self.sub_if.sw_if_index, self.dst_sub_if.sw_if_index, is_l2=1)
417 # Enable packet capturing and start packet sending
418 self.pg_enable_capture(self.pg_interfaces)
421 # Verify packets outgoing packet streams on mirrored interface (pg2)
422 self.logger.info("Verifying capture on interfaces %s and %s" %
423 (self.pg1.name, self.pg2.name))
424 pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
425 pg1_pkts = self.pg1.get_capture()
426 pg2_pkts = self.pg2.get_capture(pg2_expected)
427 pg2_untagged = [self.remove_tags(p, [Tag(dot1=DOT1Q, vlan=300)])
434 self.bridge(self.dst_sub_if.sw_if_index, is_add=0)
435 # Disable SPAN on pg0 sub if (mirrored to vxlan)
436 self.vapi.sw_interface_span_enable_disable(
437 self.sub_if.sw_if_index, self.dst_sub_if.sw_if_index, state=0,
439 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
441 def test_l2_tx_span(self):
442 """ SPAN l2 tx mirror test """
444 self.sub_if.admin_up()
445 self.bridge(self.pg2.sw_if_index)
446 # Create bi-directional cross-connects between pg0 and pg1
447 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
448 # Create incoming packet streams for packet-generator interfaces
449 pkts = self.create_stream(
450 self.pg0, self.pg_if_packet_sizes, do_dot1=True)
451 self.pg0.add_stream(pkts)
453 # Enable SPAN on pg0 (mirrored to pg2)
454 self.vapi.sw_interface_span_enable_disable(
455 self.pg1.sw_if_index, self.pg2.sw_if_index, is_l2=1, state=2)
457 self.logger.info(self.vapi.ppcli("show interface span"))
458 # Enable packet capturing and start packet sending
459 self.pg_enable_capture(self.pg_interfaces)
462 # Verify packets outgoing packet streams on mirrored interface (pg2)
463 self.logger.info("Verifying capture on interfaces %s and %s" %
464 (self.pg1.name, self.pg2.name))
465 pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
466 pg1_pkts = self.pg1.get_capture()
467 pg2_pkts = self.pg2.get_capture(pg2_expected)
473 self.bridge(self.pg2.sw_if_index, is_add=0)
474 # Disable SPAN on pg0 (mirrored to pg2)
475 self.vapi.sw_interface_span_enable_disable(
476 self.pg1.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1)
477 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
480 if __name__ == '__main__':
481 unittest.main(testRunner=VppTestRunner)