5 from scapy.packet import Raw
6 from scapy.layers.l2 import Ether, Dot1Q, GRE, ERSPAN
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.pkts_per_burst = 257 # Number of packets per burst
30 # create 3 pg interfaces
31 cls.create_pg_interfaces(range(3))
34 cls.sub_if = VppDot1QSubint(cls, cls.pg0, 100)
35 cls.vlan_sub_if = VppDot1QSubint(cls, cls.pg2, 300)
36 cls.vlan_sub_if.set_vtr(L2_VTR_OP.L2_POP_1, tag=300)
38 cls.qinq_sub_if = VppDot1ADSubint(cls, cls.pg2, 33, 400, 500)
39 cls.qinq_sub_if.set_vtr(L2_VTR_OP.L2_POP_2, outer=500, inner=400)
41 # packet flows mapping pg0 -> pg1, pg2 -> pg3, etc.
43 cls.flows[cls.pg0] = [cls.pg1]
44 cls.flows[cls.pg1] = [cls.pg0]
47 cls.pg_if_packet_sizes = [64, 512, 1518] # , 9018]
49 # setup all interfaces
50 for i in cls.pg_interfaces:
55 cls.vxlan = cls.vapi.vxlan_add_del_tunnel(
56 src_addr=cls.pg2.local_ip4n,
57 dst_addr=cls.pg2.remote_ip4n,
62 super(TestSpan, self).setUp()
63 self.reset_packet_infos()
66 super(TestSpan, self).tearDown()
68 self.logger.info(self.vapi.ppcli("show interface span"))
70 def xconnect(self, a, b, is_add=1):
71 self.vapi.sw_interface_set_l2_xconnect(a, b, enable=is_add)
72 self.vapi.sw_interface_set_l2_xconnect(b, a, enable=is_add)
74 def bridge(self, sw_if_index, is_add=1):
75 self.vapi.sw_interface_set_l2_bridge(
76 sw_if_index, bd_id=self.bd_id, enable=is_add)
78 def _remove_tag(self, packet, vlan, tag_type):
79 self.assertEqual(packet.type, tag_type)
80 payload = packet.payload
81 self.assertEqual(payload.vlan, vlan)
82 inner_type = payload.type
83 payload = payload.payload
84 packet.remove_payload()
85 packet.add_payload(payload)
86 packet.type = inner_type
88 def remove_tags(self, packet, tags):
90 self._remove_tag(packet, t.vlan, t.dot1)
93 def decap_gre(self, pkt):
95 Decapsulate the original payload frame by removing GRE header
97 self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
98 self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
100 self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
101 self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
103 return pkt[GRE].payload
105 def decap_erspan(self, pkt, session):
107 Decapsulate the original payload frame by removing ERSPAN header
109 self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
110 self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
112 self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
113 self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
115 self.assertEqual(pkt[ERSPAN].ver, 1)
116 self.assertEqual(pkt[ERSPAN].vlan, 0)
117 self.assertEqual(pkt[ERSPAN].cos, 0)
118 self.assertEqual(pkt[ERSPAN].en, 3)
119 self.assertEqual(pkt[ERSPAN].t, 0)
120 self.assertEqual(pkt[ERSPAN].session_id, session)
121 self.assertEqual(pkt[ERSPAN].reserved, 0)
122 self.assertEqual(pkt[ERSPAN].index, 0)
124 return pkt[ERSPAN].payload
126 def decap_vxlan(self, pkt):
128 Decapsulate the original payload frame by removing VXLAN header
130 self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
131 self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
133 self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
134 self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
136 return pkt[VXLAN].payload
138 def create_stream(self, src_if, packet_sizes, do_dot1=False, bcast=False):
140 dst_if = self.flows[src_if][0]
141 dst_mac = src_if.remote_mac
143 dst_mac = "ff:ff:ff:ff:ff:ff"
145 for i in range(0, self.pkts_per_burst):
146 payload = "span test"
147 size = packet_sizes[(i / 2) % len(packet_sizes)]
148 p = (Ether(src=src_if.local_mac, dst=dst_mac) /
149 IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) /
150 UDP(sport=10000 + src_if.sw_if_index * 1000 + i, dport=1234) /
153 p = self.sub_if.add_dot1_layer(p)
154 self.extend_packet(p, size)
158 def verify_capture(self, cap1, cap2):
159 self.assertEqual(len(cap1), len(cap2),
160 "Different number of sent and mirrored packets :"
161 "%u != %u" % (len(cap1), len(cap2)))
163 pkts1 = [(pkt[Ether] / pkt[IP] / pkt[UDP]) for pkt in cap1]
164 pkts2 = [(pkt[Ether] / pkt[IP] / pkt[UDP]) for pkt in cap2]
166 self.assertEqual(pkts1.sort(), pkts2.sort())
168 def test_device_span(self):
169 """ SPAN device rx mirror """
171 # Create bi-directional cross-connects between pg0 and pg1
172 self.xconnect(self.pg0.sw_if_index, self.pg1.sw_if_index)
173 # Create incoming packet streams for packet-generator interfaces
174 pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes)
175 self.pg0.add_stream(pkts)
177 # Enable SPAN on pg0 (mirrored to pg2)
178 self.vapi.sw_interface_span_enable_disable(
179 self.pg0.sw_if_index, self.pg2.sw_if_index)
181 self.logger.info(self.vapi.ppcli("show interface span"))
182 # Enable packet capturing and start packet sending
183 self.pg_enable_capture(self.pg_interfaces)
186 # Verify packets outgoing packet streams on mirrored interface (pg2)
188 pg1_pkts = self.pg1.get_capture(n_pkts)
189 pg2_pkts = self.pg2.get_capture(n_pkts)
191 # Disable SPAN on pg0 (mirrored to pg2)
192 self.vapi.sw_interface_span_enable_disable(
193 self.pg0.sw_if_index, self.pg2.sw_if_index, state=0)
194 self.xconnect(self.pg0.sw_if_index, self.pg1.sw_if_index, is_add=0)
196 self.verify_capture(pg1_pkts, pg2_pkts)
198 def test_span_l2_rx(self):
199 """ SPAN l2 rx mirror """
201 self.sub_if.admin_up()
203 self.bridge(self.pg2.sw_if_index)
204 # Create bi-directional cross-connects between pg0 subif and pg1
205 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
206 # Create incoming packet streams for packet-generator interfaces
207 pkts = self.create_stream(
208 self.pg0, self.pg_if_packet_sizes, do_dot1=True)
209 self.pg0.add_stream(pkts)
211 # Enable SPAN on pg0 (mirrored to pg2)
212 self.vapi.sw_interface_span_enable_disable(
213 self.sub_if.sw_if_index, self.pg2.sw_if_index, is_l2=1)
215 self.logger.info(self.vapi.ppcli("show interface span"))
216 # Enable packet capturing and start packet sending
217 self.pg_enable_capture(self.pg_interfaces)
220 # Verify packets outgoing packet streams on mirrored interface (pg2)
221 pg2_expected = len(pkts)
222 pg1_pkts = self.pg1.get_capture(pg2_expected)
223 pg2_pkts = self.pg2.get_capture(pg2_expected)
224 self.bridge(self.pg2.sw_if_index, is_add=0)
226 # Disable SPAN on pg0 (mirrored to pg2)
227 self.vapi.sw_interface_span_enable_disable(
228 self.sub_if.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1)
229 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
231 self.verify_capture(pg1_pkts, pg2_pkts)
233 def test_span_l2_rx_dst_vxlan(self):
234 """ SPAN l2 rx mirror into vxlan """
236 self.sub_if.admin_up()
237 self.vapi.sw_interface_set_flags(self.vxlan.sw_if_index,
240 self.bridge(self.vxlan.sw_if_index, is_add=1)
241 # Create bi-directional cross-connects between pg0 subif and pg1
242 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
243 # Create incoming packet streams for packet-generator interfaces
244 pkts = self.create_stream(
245 self.pg0, self.pg_if_packet_sizes, do_dot1=True)
246 self.pg0.add_stream(pkts)
248 # Enable SPAN on pg0 sub if (mirrored to vxlan)
249 self.vapi.sw_interface_span_enable_disable(
250 self.sub_if.sw_if_index, self.vxlan.sw_if_index, is_l2=1)
252 self.logger.info(self.vapi.ppcli("show interface span"))
253 # Enable packet capturing and start packet sending
254 self.pg_enable_capture(self.pg_interfaces)
257 # Verify packets outgoing packet streams on mirrored interface (pg2)
259 pg1_pkts = self.pg1.get_capture(n_pkts)
260 pg2_pkts = [self.decap_vxlan(p) for p in self.pg2.get_capture(n_pkts)]
262 self.bridge(self.vxlan.sw_if_index, is_add=0)
263 # Disable SPAN on pg0 sub if (mirrored to vxlan)
264 self.vapi.sw_interface_span_enable_disable(
265 self.sub_if.sw_if_index, self.vxlan.sw_if_index, state=0, is_l2=1)
266 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
267 self.verify_capture(pg1_pkts, pg2_pkts)
269 def test_span_l2_rx_dst_gre_erspan(self):
270 """ SPAN l2 rx mirror into gre-erspan """
272 self.sub_if.admin_up()
274 gre_if = VppGreInterface(self, self.pg2.local_ip4,
279 gre_if.add_vpp_config()
282 self.bridge(gre_if.sw_if_index)
283 # Create bi-directional cross-connects between pg0 and pg1
284 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
286 # Create incoming packet streams for packet-generator interfaces
287 pkts = self.create_stream(
288 self.pg0, self.pg_if_packet_sizes, do_dot1=True)
289 self.pg0.add_stream(pkts)
291 # Enable SPAN on pg0 sub if (mirrored to gre-erspan)
292 self.vapi.sw_interface_span_enable_disable(
293 self.sub_if.sw_if_index, gre_if.sw_if_index, is_l2=1)
295 # Enable packet capturing and start packet sending
296 self.pg_enable_capture(self.pg_interfaces)
299 # Verify packets outgoing packet streams on mirrored interface (pg2)
301 pg1_pkts = self.pg1.get_capture(n_pkts)
302 pg2_pkts = self.pg2.get_capture(n_pkts)
304 def decap(p): return self.decap_erspan(p, session=543)
305 pg2_decaped = [decap(p) for p in pg2_pkts]
307 self.bridge(gre_if.sw_if_index, is_add=0)
309 # Disable SPAN on pg0 sub if
310 self.vapi.sw_interface_span_enable_disable(
311 self.sub_if.sw_if_index, gre_if.sw_if_index, state=0, is_l2=1)
312 gre_if.remove_vpp_config()
313 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
315 self.verify_capture(pg1_pkts, pg2_decaped)
317 def test_span_l2_rx_dst_gre_subif_vtr(self):
318 """ SPAN l2 rx mirror into gre-subif+vtr """
320 self.sub_if.admin_up()
322 gre_if = VppGreInterface(self, self.pg2.local_ip4,
326 gre_if.add_vpp_config()
329 gre_sub_if = VppDot1QSubint(self, gre_if, 500)
330 gre_sub_if.set_vtr(L2_VTR_OP.L2_POP_1, tag=500)
331 gre_sub_if.admin_up()
333 self.bridge(gre_sub_if.sw_if_index)
334 # Create bi-directional cross-connects between pg0 and pg1
335 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
337 # Create incoming packet streams for packet-generator interfaces
338 pkts = self.create_stream(
339 self.pg0, self.pg_if_packet_sizes, do_dot1=True)
340 self.pg0.add_stream(pkts)
342 # Enable SPAN on pg0 sub if (mirrored to gre sub if)
343 self.vapi.sw_interface_span_enable_disable(
344 self.sub_if.sw_if_index, gre_sub_if.sw_if_index, is_l2=1)
346 # Enable packet capturing and start packet sending
347 self.pg_enable_capture(self.pg_interfaces)
350 # Verify packets outgoing packet streams on mirrored interface (pg2)
352 pg1_pkts = self.pg1.get_capture(n_pkts)
353 pg2_pkts = self.pg2.get_capture(n_pkts)
355 def decap(p): return self.remove_tags(
356 self.decap_gre(p), [Tag(dot1=DOT1Q, vlan=500)])
357 pg2_decaped = [decap(p) for p in pg2_pkts]
359 self.bridge(gre_sub_if.sw_if_index, is_add=0)
361 # Disable SPAN on pg0 sub if
362 self.vapi.sw_interface_span_enable_disable(
363 self.sub_if.sw_if_index, gre_sub_if.sw_if_index, state=0, is_l2=1)
364 gre_if.remove_vpp_config()
365 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
367 self.verify_capture(pg1_pkts, pg2_decaped)
369 def test_span_l2_rx_dst_1q_vtr(self):
370 """ SPAN l2 rx mirror into 1q subif+vtr """
372 self.sub_if.admin_up()
373 self.vlan_sub_if.admin_up()
375 self.bridge(self.vlan_sub_if.sw_if_index)
376 # Create bi-directional cross-connects between pg0 and pg1
377 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
379 # Create incoming packet streams for packet-generator interfaces
380 pkts = self.create_stream(
381 self.pg0, self.pg_if_packet_sizes, do_dot1=True)
382 self.pg0.add_stream(pkts)
384 self.vapi.sw_interface_span_enable_disable(
385 self.sub_if.sw_if_index, self.vlan_sub_if.sw_if_index, is_l2=1)
387 # Enable packet capturing and start packet sending
388 self.pg_enable_capture(self.pg_interfaces)
391 # Verify packets outgoing packet streams on mirrored interface (pg2)
393 pg1_pkts = self.pg1.get_capture(n_pkts)
394 pg2_pkts = self.pg2.get_capture(n_pkts)
395 pg2_untagged = [self.remove_tags(p, [Tag(dot1=DOT1Q, vlan=300)])
398 self.bridge(self.vlan_sub_if.sw_if_index, is_add=0)
399 # Disable SPAN on pg0 sub if (mirrored to vxlan)
400 self.vapi.sw_interface_span_enable_disable(
401 self.sub_if.sw_if_index, self.vlan_sub_if.sw_if_index, state=0,
403 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
405 self.verify_capture(pg1_pkts, pg2_untagged)
407 def test_span_l2_rx_dst_1ad_vtr(self):
408 """ SPAN l2 rx mirror into 1ad subif+vtr """
410 self.sub_if.admin_up()
411 self.qinq_sub_if.admin_up()
413 self.bridge(self.qinq_sub_if.sw_if_index)
414 # Create bi-directional cross-connects between pg0 and pg1
415 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
417 # Create incoming packet streams for packet-generator interfaces
418 pkts = self.create_stream(
419 self.pg0, self.pg_if_packet_sizes, do_dot1=True)
420 self.pg0.add_stream(pkts)
422 self.vapi.sw_interface_span_enable_disable(
423 self.sub_if.sw_if_index, self.qinq_sub_if.sw_if_index, is_l2=1)
425 # Enable packet capturing and start packet sending
426 self.pg_enable_capture(self.pg_interfaces)
429 # Verify packets outgoing packet streams on mirrored interface (pg2)
431 pg1_pkts = self.pg1.get_capture(n_pkts)
432 pg2_pkts = self.pg2.get_capture(n_pkts)
433 pg2_untagged = [self.remove_tags(p, [Tag(dot1=DOT1AD, vlan=400),
434 Tag(dot1=DOT1Q, vlan=500)])
437 self.bridge(self.qinq_sub_if.sw_if_index, is_add=0)
438 # Disable SPAN on pg0 sub if (mirrored to vxlan)
439 self.vapi.sw_interface_span_enable_disable(
440 self.sub_if.sw_if_index, self.qinq_sub_if.sw_if_index, state=0,
442 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
444 self.verify_capture(pg1_pkts, pg2_untagged)
446 def test_l2_tx_span(self):
447 """ SPAN l2 tx mirror """
449 self.sub_if.admin_up()
450 self.bridge(self.pg2.sw_if_index)
451 # Create bi-directional cross-connects between pg0 and pg1
452 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
453 # Create incoming packet streams for packet-generator interfaces
454 pkts = self.create_stream(
455 self.pg0, self.pg_if_packet_sizes, do_dot1=True)
456 self.pg0.add_stream(pkts)
458 # Enable SPAN on pg1 (mirrored to pg2)
459 self.vapi.sw_interface_span_enable_disable(
460 self.pg1.sw_if_index, self.pg2.sw_if_index, is_l2=1, state=2)
462 self.logger.info(self.vapi.ppcli("show interface span"))
463 # Enable packet capturing and start packet sending
464 self.pg_enable_capture(self.pg_interfaces)
467 # Verify packets outgoing packet streams on mirrored interface (pg2)
469 pg1_pkts = self.pg1.get_capture(n_pkts)
470 pg2_pkts = self.pg2.get_capture(n_pkts)
471 self.bridge(self.pg2.sw_if_index, is_add=0)
472 # Disable SPAN on pg0 (mirrored to pg2)
473 self.vapi.sw_interface_span_enable_disable(
474 self.pg1.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1)
475 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
477 self.verify_capture(pg1_pkts, pg2_pkts)
479 def test_l2_rx_tx_span(self):
480 """ SPAN l2 rx tx mirror """
482 self.sub_if.admin_up()
483 self.bridge(self.pg2.sw_if_index)
484 # Create bi-directional cross-connects between pg0 and pg1
485 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
487 # Create incoming packet streams for packet-generator interfaces
488 pg0_pkts = self.create_stream(
489 self.pg0, self.pg_if_packet_sizes, do_dot1=True)
490 self.pg0.add_stream(pg0_pkts)
491 pg1_pkts = self.create_stream(
492 self.pg1, self.pg_if_packet_sizes, do_dot1=False)
493 self.pg1.add_stream(pg1_pkts)
495 # Enable SPAN on pg0 (mirrored to pg2)
496 self.vapi.sw_interface_span_enable_disable(
497 self.sub_if.sw_if_index, self.pg2.sw_if_index, is_l2=1, state=3)
498 self.logger.info(self.vapi.ppcli("show interface span"))
500 # Enable packet capturing and start packet sending
501 self.pg_enable_capture(self.pg_interfaces)
504 # Verify packets outgoing packet streams on mirrored interface (pg2)
505 pg0_expected = len(pg1_pkts)
506 pg1_expected = len(pg0_pkts)
507 pg2_expected = pg0_expected + pg1_expected
509 pg0_pkts = self.pg0.get_capture(pg0_expected)
510 pg1_pkts = self.pg1.get_capture(pg1_expected)
511 pg2_pkts = self.pg2.get_capture(pg2_expected)
513 self.bridge(self.pg2.sw_if_index, is_add=0)
514 # Disable SPAN on pg0 (mirrored to pg2)
515 self.vapi.sw_interface_span_enable_disable(
516 self.sub_if.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1)
517 self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
519 self.verify_capture(pg0_pkts + pg1_pkts, pg2_pkts)
521 def test_l2_bcast_mirror(self):
522 """ SPAN l2 broadcast mirror """
524 self.sub_if.admin_up()
525 self.bridge(self.pg2.sw_if_index)
527 # Create bi-directional cross-connects between pg0 and pg1
528 self.vapi.sw_interface_set_l2_bridge(
529 self.sub_if.sw_if_index, bd_id=99, enable=1)
530 self.vapi.sw_interface_set_l2_bridge(
531 self.pg1.sw_if_index, bd_id=99, enable=1)
533 # Create incoming packet streams for packet-generator interfaces
534 pg0_pkts = self.create_stream(
535 self.pg0, self.pg_if_packet_sizes, do_dot1=True, bcast=True)
536 self.pg0.add_stream(pg0_pkts)
537 pg1_pkts = self.create_stream(
538 self.pg1, self.pg_if_packet_sizes, do_dot1=False, bcast=True)
539 self.pg1.add_stream(pg1_pkts)
541 # Enable SPAN on pg0 (mirrored to pg2)
542 self.vapi.sw_interface_span_enable_disable(
543 self.sub_if.sw_if_index, self.pg2.sw_if_index, is_l2=1, state=3)
544 self.logger.info(self.vapi.ppcli("show interface span"))
546 # Enable packet capturing and start packet sending
547 self.pg_enable_capture(self.pg_interfaces)
550 # Verify packets outgoing packet streams on mirrored interface (pg2)
551 pg0_expected = len(pg1_pkts)
552 pg1_expected = len(pg0_pkts)
553 pg2_expected = pg0_expected + pg1_expected
555 pg0_pkts = self.pg0.get_capture(pg0_expected)
556 pg1_pkts = self.pg1.get_capture(pg1_expected)
557 pg2_pkts = self.pg2.get_capture(pg2_expected)
559 self.bridge(self.pg2.sw_if_index, is_add=0)
560 self.vapi.sw_interface_set_l2_bridge(
561 self.sub_if.sw_if_index, bd_id=99, enable=0)
562 self.vapi.sw_interface_set_l2_bridge(
563 self.pg1.sw_if_index, bd_id=99, enable=0)
564 # Disable SPAN on pg0 (mirrored to pg2)
565 self.vapi.sw_interface_span_enable_disable(
566 self.sub_if.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1)
568 self.verify_capture(pg0_pkts + pg1_pkts, pg2_pkts)
571 if __name__ == '__main__':
572 unittest.main(testRunner=VppTestRunner)