5 from scapy.packet import Raw
6 from scapy.layers.l2 import Ether, GRE, ERSPAN
7 from scapy.layers.inet import IP, UDP
8 from scapy.layers.vxlan import VXLAN
10 from framework import VppTestCase
11 from asfframework import VppTestRunner
12 from vpp_sub_interface import L2_VTR_OP, VppDot1QSubint, VppDot1ADSubint
13 from vpp_gre_interface import VppGreInterface
14 from vpp_vxlan_tunnel import VppVxlanTunnel
15 from collections import namedtuple
16 from vpp_papi import VppEnum
19 Tag = namedtuple("Tag", ["dot1", "vlan"])
24 class TestSpan(VppTestCase):
29 super(TestSpan, cls).setUpClass()
31 cls.pkts_per_burst = 257 # Number of packets per burst
32 # create 3 pg interfaces
33 cls.create_pg_interfaces(range(3))
36 cls.sub_if = VppDot1QSubint(cls, cls.pg0, 100)
37 cls.vlan_sub_if = VppDot1QSubint(cls, cls.pg2, 300)
38 cls.vlan_sub_if.set_vtr(L2_VTR_OP.L2_POP_1, tag=300)
40 cls.qinq_sub_if = VppDot1ADSubint(cls, cls.pg2, 33, 400, 500)
41 cls.qinq_sub_if.set_vtr(L2_VTR_OP.L2_POP_2, outer=500, inner=400)
43 # packet flows mapping pg0 -> pg1, pg2 -> pg3, etc.
45 cls.flows[cls.pg0] = [cls.pg1]
46 cls.flows[cls.pg1] = [cls.pg0]
49 cls.pg_if_packet_sizes = [64, 512, 1518] # , 9018]
51 # setup all interfaces
52 for i in cls.pg_interfaces:
58 super(TestSpan, self).setUp()
59 self.vxlan = VppVxlanTunnel(
60 self, src=self.pg2.local_ip4, dst=self.pg2.remote_ip4, vni=1111
62 self.vxlan.add_vpp_config()
63 self.reset_packet_infos()
66 super(TestSpan, self).tearDown()
68 def show_commands_at_teardown(self):
69 self.logger.info(self.vapi.ppcli("show interface span"))
71 def xconnect(self, a, b, is_add=1):
72 self.vapi.sw_interface_set_l2_xconnect(a, b, enable=is_add)
73 self.vapi.sw_interface_set_l2_xconnect(b, a, enable=is_add)
75 def bridge(self, sw_if_index, is_add=1):
76 self.vapi.sw_interface_set_l2_bridge(
77 rx_sw_if_index=sw_if_index, bd_id=self.bd_id, enable=is_add
80 def _remove_tag(self, packet, vlan, tag_type):
81 self.assertEqual(packet.type, tag_type)
82 payload = packet.payload
83 self.assertEqual(payload.vlan, vlan)
84 inner_type = payload.type
85 payload = payload.payload
86 packet.remove_payload()
87 packet.add_payload(payload)
88 packet.type = inner_type
90 def remove_tags(self, packet, tags):
92 self._remove_tag(packet, t.vlan, t.dot1)
95 def decap_gre(self, pkt):
97 Decapsulate the original payload frame by removing GRE header
99 self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
100 self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
102 self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
103 self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
105 return pkt[GRE].payload
107 def decap_erspan(self, pkt, session):
109 Decapsulate the original payload frame by removing ERSPAN header
111 self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
112 self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
114 self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
115 self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
117 self.assertEqual(pkt[ERSPAN].ver, 1)
118 self.assertEqual(pkt[ERSPAN].vlan, 0)
119 self.assertEqual(pkt[ERSPAN].cos, 0)
120 self.assertEqual(pkt[ERSPAN].en, 3)
121 self.assertEqual(pkt[ERSPAN].t, 0)
122 self.assertEqual(pkt[ERSPAN].session_id, session)
123 self.assertEqual(pkt[ERSPAN].reserved, 0)
124 self.assertEqual(pkt[ERSPAN].index, 0)
126 return pkt[ERSPAN].payload
128 def decap_vxlan(self, pkt):
130 Decapsulate the original payload frame by removing VXLAN header
132 self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
133 self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
135 self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
136 self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
138 return pkt[VXLAN].payload
140 def create_stream(self, src_if, packet_sizes, do_dot1=False, bcast=False):
142 dst_if = self.flows[src_if][0]
143 dst_mac = src_if.remote_mac
145 dst_mac = "ff:ff:ff:ff:ff:ff"
147 for i in range(0, self.pkts_per_burst):
148 payload = "span test"
149 size = packet_sizes[int((i / 2) % len(packet_sizes))]
151 Ether(src=src_if.local_mac, dst=dst_mac)
152 / IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4)
153 / UDP(sport=10000 + src_if.sw_if_index * 1000 + i, dport=1234)
157 p = self.sub_if.add_dot1_layer(p)
158 self.extend_packet(p, size)
162 def verify_capture(self, cap1, cap2):
166 "Different number of sent and mirrored packets :"
167 "%u != %u" % (len(cap1), len(cap2)),
170 pkts1 = [(pkt[Ether] / pkt[IP] / pkt[UDP]) for pkt in cap1]
171 pkts2 = [(pkt[Ether] / pkt[IP] / pkt[UDP]) for pkt in cap2]
173 self.assertEqual(pkts1.sort(), pkts2.sort())
175 def test_device_span(self):
176 """SPAN device rx mirror"""
178 # Create bi-directional cross-connects between pg0 and pg1
179 self.xconnect(self.pg0.sw_if_index, self.pg1.sw_if_index)
180 # Create incoming packet streams for packet-generator interfaces
181 pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes)
182 self.pg0.add_stream(pkts)
184 # Enable SPAN on pg0 (mirrored to pg2)
185 self.vapi.sw_interface_span_enable_disable(
186 self.pg0.sw_if_index, self.pg2.sw_if_index
189 self.logger.info(self.vapi.ppcli("show interface span"))
190 # Enable packet capturing and start packet sending
191 self.pg_enable_capture(self.pg_interfaces)
194 # Verify packets outgoing packet streams on mirrored interface (pg2)
196 pg1_pkts = self.pg1.get_capture(n_pkts)
197 pg2_pkts = self.pg2.get_capture(n_pkts)
199 # Disable SPAN on pg0 (mirrored to pg2)
200 self.vapi.sw_interface_span_enable_disable(
201 self.pg0.sw_if_index, self.pg2.sw_if_index, state=0
203 self.xconnect(self.pg0.sw_if_index, self.pg1.sw_if_index, is_add=0)
205 self.verify_capture(pg1_pkts, pg2_pkts)
207 def test_span_l2_rx(self):
208 """SPAN l2 rx mirror"""
210 self.sub_if.admin_up()
212 self.bridge(self.pg2.sw_if_index)
213 # Create bi-directional cross-connects between pg0 subif and pg1
214 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
215 # Create incoming packet streams for packet-generator interfaces
216 pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes, do_dot1=True)
217 self.pg0.add_stream(pkts)
219 # Enable SPAN on pg0 (mirrored to pg2)
220 self.vapi.sw_interface_span_enable_disable(
221 self.sub_if.sw_if_index, self.pg2.sw_if_index, is_l2=1
224 self.logger.info(self.vapi.ppcli("show interface span"))
225 # Enable packet capturing and start packet sending
226 self.pg_enable_capture(self.pg_interfaces)
229 # Verify packets outgoing packet streams on mirrored interface (pg2)
230 pg2_expected = len(pkts)
231 pg1_pkts = self.pg1.get_capture(pg2_expected)
232 pg2_pkts = self.pg2.get_capture(pg2_expected)
233 self.bridge(self.pg2.sw_if_index, is_add=0)
235 # Disable SPAN on pg0 (mirrored to pg2)
236 self.vapi.sw_interface_span_enable_disable(
237 self.sub_if.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1
239 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
241 self.verify_capture(pg1_pkts, pg2_pkts)
243 def test_span_l2_rx_dst_vxlan(self):
244 """SPAN l2 rx mirror into vxlan"""
246 self.sub_if.admin_up()
247 self.vapi.sw_interface_set_flags(self.vxlan.sw_if_index, flags=1)
249 self.bridge(self.vxlan.sw_if_index, is_add=1)
250 # Create bi-directional cross-connects between pg0 subif and pg1
251 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
252 # Create incoming packet streams for packet-generator interfaces
253 pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes, do_dot1=True)
254 self.pg0.add_stream(pkts)
256 # Enable SPAN on pg0 sub if (mirrored to vxlan)
257 self.vapi.sw_interface_span_enable_disable(
258 self.sub_if.sw_if_index, self.vxlan.sw_if_index, is_l2=1
261 self.logger.info(self.vapi.ppcli("show interface span"))
262 # Enable packet capturing and start packet sending
263 self.pg_enable_capture(self.pg_interfaces)
266 # Verify packets outgoing packet streams on mirrored interface (pg2)
268 pg1_pkts = self.pg1.get_capture(n_pkts)
269 pg2_pkts = [self.decap_vxlan(p) for p in self.pg2.get_capture(n_pkts)]
271 self.bridge(self.vxlan.sw_if_index, is_add=0)
272 # Disable SPAN on pg0 sub if (mirrored to vxlan)
273 self.vapi.sw_interface_span_enable_disable(
274 self.sub_if.sw_if_index, self.vxlan.sw_if_index, state=0, is_l2=1
276 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
277 self.verify_capture(pg1_pkts, pg2_pkts)
279 def test_span_l2_rx_dst_gre_erspan(self):
280 """SPAN l2 rx mirror into gre-erspan"""
282 self.sub_if.admin_up()
284 gre_if = VppGreInterface(
289 type=(VppEnum.vl_api_gre_tunnel_type_t.GRE_API_TUNNEL_TYPE_ERSPAN),
292 gre_if.add_vpp_config()
295 self.bridge(gre_if.sw_if_index)
296 # Create bi-directional cross-connects between pg0 and pg1
297 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
299 # Create incoming packet streams for packet-generator interfaces
300 pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes, do_dot1=True)
301 self.pg0.add_stream(pkts)
303 # Enable SPAN on pg0 sub if (mirrored to gre-erspan)
304 self.vapi.sw_interface_span_enable_disable(
305 self.sub_if.sw_if_index, gre_if.sw_if_index, is_l2=1
308 # Enable packet capturing and start packet sending
309 self.pg_enable_capture(self.pg_interfaces)
312 # Verify packets outgoing packet streams on mirrored interface (pg2)
314 pg1_pkts = self.pg1.get_capture(n_pkts)
315 pg2_pkts = self.pg2.get_capture(n_pkts)
318 return self.decap_erspan(p, session=543)
320 pg2_decaped = [decap(p) for p in pg2_pkts]
322 self.bridge(gre_if.sw_if_index, is_add=0)
324 # Disable SPAN on pg0 sub if
325 self.vapi.sw_interface_span_enable_disable(
326 self.sub_if.sw_if_index, gre_if.sw_if_index, state=0, is_l2=1
328 gre_if.remove_vpp_config()
329 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
331 self.verify_capture(pg1_pkts, pg2_decaped)
333 def test_span_l2_rx_dst_gre_subif_vtr(self):
334 """SPAN l2 rx mirror into gre-subif+vtr"""
336 self.sub_if.admin_up()
338 gre_if = VppGreInterface(
342 type=(VppEnum.vl_api_gre_tunnel_type_t.GRE_API_TUNNEL_TYPE_TEB),
345 gre_if.add_vpp_config()
348 gre_sub_if = VppDot1QSubint(self, gre_if, 500)
349 gre_sub_if.set_vtr(L2_VTR_OP.L2_POP_1, tag=500)
350 gre_sub_if.admin_up()
352 self.bridge(gre_sub_if.sw_if_index)
353 # Create bi-directional cross-connects between pg0 and pg1
354 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
356 # Create incoming packet streams for packet-generator interfaces
357 pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes, do_dot1=True)
358 self.pg0.add_stream(pkts)
360 # Enable SPAN on pg0 sub if (mirrored to gre sub if)
361 self.vapi.sw_interface_span_enable_disable(
362 self.sub_if.sw_if_index, gre_sub_if.sw_if_index, is_l2=1
365 # Enable packet capturing and start packet sending
366 self.pg_enable_capture(self.pg_interfaces)
369 # Verify packets outgoing packet streams on mirrored interface (pg2)
371 pg1_pkts = self.pg1.get_capture(n_pkts)
372 pg2_pkts = self.pg2.get_capture(n_pkts)
375 return self.remove_tags(self.decap_gre(p), [Tag(dot1=DOT1Q, vlan=500)])
377 pg2_decaped = [decap(p) for p in pg2_pkts]
379 self.bridge(gre_sub_if.sw_if_index, is_add=0)
381 # Disable SPAN on pg0 sub if
382 self.vapi.sw_interface_span_enable_disable(
383 self.sub_if.sw_if_index, gre_sub_if.sw_if_index, state=0, is_l2=1
385 gre_if.remove_vpp_config()
386 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
388 self.verify_capture(pg1_pkts, pg2_decaped)
390 def test_span_l2_rx_dst_1q_vtr(self):
391 """SPAN l2 rx mirror into 1q subif+vtr"""
393 self.sub_if.admin_up()
394 self.vlan_sub_if.admin_up()
396 self.bridge(self.vlan_sub_if.sw_if_index)
397 # Create bi-directional cross-connects between pg0 and pg1
398 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
400 # Create incoming packet streams for packet-generator interfaces
401 pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes, do_dot1=True)
402 self.pg0.add_stream(pkts)
404 self.vapi.sw_interface_span_enable_disable(
405 self.sub_if.sw_if_index, self.vlan_sub_if.sw_if_index, is_l2=1
408 # Enable packet capturing and start packet sending
409 self.pg_enable_capture(self.pg_interfaces)
412 # Verify packets outgoing packet streams on mirrored interface (pg2)
414 pg1_pkts = self.pg1.get_capture(n_pkts)
415 pg2_pkts = self.pg2.get_capture(n_pkts)
417 self.remove_tags(p, [Tag(dot1=DOT1Q, vlan=300)]) for p in pg2_pkts
420 self.bridge(self.vlan_sub_if.sw_if_index, is_add=0)
421 # Disable SPAN on pg0 sub if (mirrored to vxlan)
422 self.vapi.sw_interface_span_enable_disable(
423 self.sub_if.sw_if_index, self.vlan_sub_if.sw_if_index, state=0, is_l2=1
425 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
427 self.verify_capture(pg1_pkts, pg2_untagged)
429 def test_span_l2_rx_dst_1ad_vtr(self):
430 """SPAN l2 rx mirror into 1ad subif+vtr"""
432 self.sub_if.admin_up()
433 self.qinq_sub_if.admin_up()
435 self.bridge(self.qinq_sub_if.sw_if_index)
436 # Create bi-directional cross-connects between pg0 and pg1
437 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
439 # Create incoming packet streams for packet-generator interfaces
440 pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes, do_dot1=True)
441 self.pg0.add_stream(pkts)
443 self.vapi.sw_interface_span_enable_disable(
444 self.sub_if.sw_if_index, self.qinq_sub_if.sw_if_index, is_l2=1
447 # Enable packet capturing and start packet sending
448 self.pg_enable_capture(self.pg_interfaces)
451 # Verify packets outgoing packet streams on mirrored interface (pg2)
453 pg1_pkts = self.pg1.get_capture(n_pkts)
454 pg2_pkts = self.pg2.get_capture(n_pkts)
456 self.remove_tags(p, [Tag(dot1=DOT1AD, vlan=400), Tag(dot1=DOT1Q, vlan=500)])
460 self.bridge(self.qinq_sub_if.sw_if_index, is_add=0)
461 # Disable SPAN on pg0 sub if (mirrored to vxlan)
462 self.vapi.sw_interface_span_enable_disable(
463 self.sub_if.sw_if_index, self.qinq_sub_if.sw_if_index, state=0, is_l2=1
465 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
467 self.verify_capture(pg1_pkts, pg2_untagged)
469 def test_l2_tx_span(self):
470 """SPAN l2 tx mirror"""
472 self.sub_if.admin_up()
473 self.bridge(self.pg2.sw_if_index)
474 # Create bi-directional cross-connects between pg0 and pg1
475 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
476 # Create incoming packet streams for packet-generator interfaces
477 pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes, do_dot1=True)
478 self.pg0.add_stream(pkts)
480 # Enable SPAN on pg1 (mirrored to pg2)
481 self.vapi.sw_interface_span_enable_disable(
482 self.pg1.sw_if_index, self.pg2.sw_if_index, is_l2=1, state=2
485 self.logger.info(self.vapi.ppcli("show interface span"))
486 # Enable packet capturing and start packet sending
487 self.pg_enable_capture(self.pg_interfaces)
490 # Verify packets outgoing packet streams on mirrored interface (pg2)
492 pg1_pkts = self.pg1.get_capture(n_pkts)
493 pg2_pkts = self.pg2.get_capture(n_pkts)
494 self.bridge(self.pg2.sw_if_index, is_add=0)
495 # Disable SPAN on pg0 (mirrored to pg2)
496 self.vapi.sw_interface_span_enable_disable(
497 self.pg1.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1
499 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
501 self.verify_capture(pg1_pkts, pg2_pkts)
503 def test_l2_rx_tx_span(self):
504 """SPAN l2 rx tx mirror"""
506 self.sub_if.admin_up()
507 self.bridge(self.pg2.sw_if_index)
508 # Create bi-directional cross-connects between pg0 and pg1
509 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
511 # Create incoming packet streams for packet-generator interfaces
512 pg0_pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes, do_dot1=True)
513 self.pg0.add_stream(pg0_pkts)
514 pg1_pkts = self.create_stream(self.pg1, self.pg_if_packet_sizes, do_dot1=False)
515 self.pg1.add_stream(pg1_pkts)
517 # Enable SPAN on pg0 (mirrored to pg2)
518 self.vapi.sw_interface_span_enable_disable(
519 self.sub_if.sw_if_index, self.pg2.sw_if_index, is_l2=1, state=3
521 self.logger.info(self.vapi.ppcli("show interface span"))
523 # Enable packet capturing and start packet sending
524 self.pg_enable_capture(self.pg_interfaces)
527 # Verify packets outgoing packet streams on mirrored interface (pg2)
528 pg0_expected = len(pg1_pkts)
529 pg1_expected = len(pg0_pkts)
530 pg2_expected = pg0_expected + pg1_expected
532 pg0_pkts = self.pg0.get_capture(pg0_expected)
533 pg1_pkts = self.pg1.get_capture(pg1_expected)
534 pg2_pkts = self.pg2.get_capture(pg2_expected)
536 self.bridge(self.pg2.sw_if_index, is_add=0)
537 # Disable SPAN on pg0 (mirrored to pg2)
538 self.vapi.sw_interface_span_enable_disable(
539 self.sub_if.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1
541 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
543 self.verify_capture(pg0_pkts + pg1_pkts, pg2_pkts)
545 def test_l2_bcast_mirror(self):
546 """SPAN l2 broadcast mirror"""
548 self.sub_if.admin_up()
549 self.bridge(self.pg2.sw_if_index)
551 # Create bi-directional cross-connects between pg0 and pg1
552 self.vapi.sw_interface_set_l2_bridge(
553 rx_sw_if_index=self.sub_if.sw_if_index, bd_id=99, enable=1
555 self.vapi.sw_interface_set_l2_bridge(
556 rx_sw_if_index=self.pg1.sw_if_index, bd_id=99, enable=1
559 # Create incoming packet streams for packet-generator interfaces
560 pg0_pkts = self.create_stream(
561 self.pg0, self.pg_if_packet_sizes, do_dot1=True, bcast=True
563 self.pg0.add_stream(pg0_pkts)
564 pg1_pkts = self.create_stream(
565 self.pg1, self.pg_if_packet_sizes, do_dot1=False, bcast=True
567 self.pg1.add_stream(pg1_pkts)
569 # Enable SPAN on pg0 (mirrored to pg2)
570 self.vapi.sw_interface_span_enable_disable(
571 self.sub_if.sw_if_index, self.pg2.sw_if_index, is_l2=1, state=3
573 self.logger.info(self.vapi.ppcli("show interface span"))
575 # Enable packet capturing and start packet sending
576 self.pg_enable_capture(self.pg_interfaces)
579 # Verify packets outgoing packet streams on mirrored interface (pg2)
580 pg0_expected = len(pg1_pkts)
581 pg1_expected = len(pg0_pkts)
582 pg2_expected = pg0_expected + pg1_expected
584 pg0_pkts = self.pg0.get_capture(pg0_expected)
585 pg1_pkts = self.pg1.get_capture(pg1_expected)
586 pg2_pkts = self.pg2.get_capture(pg2_expected)
588 self.bridge(self.pg2.sw_if_index, is_add=0)
589 self.vapi.sw_interface_set_l2_bridge(
590 rx_sw_if_index=self.sub_if.sw_if_index, bd_id=99, enable=0
592 self.vapi.sw_interface_set_l2_bridge(
593 rx_sw_if_index=self.pg1.sw_if_index, bd_id=99, enable=0
595 # Disable SPAN on pg0 (mirrored to pg2)
596 self.vapi.sw_interface_span_enable_disable(
597 self.sub_if.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1
600 self.verify_capture(pg0_pkts + pg1_pkts, pg2_pkts)
603 if __name__ == "__main__":
604 unittest.main(testRunner=VppTestRunner)