5 from framework import VppTestCase
6 from asfframework import VppTestRunner
7 from vpp_ip import DpoProto
8 from vpp_ip_route import VppIpRoute, VppRoutePath
12 from scapy.packet import Raw
13 from scapy.layers.l2 import Ether, Dot1Q
14 from scapy.layers.inet6 import IPv6, UDP, IPv6ExtHdrSegmentRouting
15 from scapy.layers.inet import IP, UDP
20 class TestSRv6Ad(VppTestCase):
21 """SRv6 Dynamic Proxy plugin Test Case"""
25 super(TestSRv6Ad, self).setUpClass()
28 def tearDownClass(cls):
29 super(TestSRv6Ad, cls).tearDownClass()
32 """Perform test setup before each test case."""
33 super(TestSRv6Ad, self).setUp()
35 # packet sizes, inclusive L2 overhead
36 self.pg_packet_sizes = [64, 512, 1518, 9018]
39 self.reset_packet_infos()
42 """Clean up test setup after each test case."""
43 self.teardown_interfaces()
45 super(TestSRv6Ad, self).tearDown()
47 def configure_interface(
48 self, interface, ipv6=False, ipv4=False, ipv6_table_id=0, ipv4_table_id=0
50 """Configure interface.
51 :param ipv6: configure IPv6 on interface
52 :param ipv4: configure IPv4 on interface
53 :param ipv6_table_id: FIB table_id for IPv6
54 :param ipv4_table_id: FIB table_id for IPv4
56 self.logger.debug("Configuring interface %s" % (interface.name))
58 self.logger.debug("Configuring IPv6")
59 interface.set_table_ip6(ipv6_table_id)
60 interface.config_ip6()
61 interface.resolve_ndp(timeout=5)
63 self.logger.debug("Configuring IPv4")
64 interface.set_table_ip4(ipv4_table_id)
65 interface.config_ip4()
66 interface.resolve_arp()
69 def setup_interfaces(self, ipv6=[], ipv4=[], ipv6_table_id=[], ipv4_table_id=[]):
70 """Create and configure interfaces.
72 :param ipv6: list of interface IPv6 capabilities
73 :param ipv4: list of interface IPv4 capabilities
74 :param ipv6_table_id: list of intf IPv6 FIB table_ids
75 :param ipv4_table_id: list of intf IPv4 FIB table_ids
76 :returns: List of created interfaces.
78 # how many interfaces?
83 self.logger.debug("Creating and configuring %d interfaces" % (count))
85 # fill up ipv6 and ipv4 lists if needed
86 # not enabled (False) is the default
88 ipv6 += (count - len(ipv6)) * [False]
90 ipv4 += (count - len(ipv4)) * [False]
92 # fill up table_id lists if needed
93 # table_id 0 (global) is the default
94 if len(ipv6_table_id) < count:
95 ipv6_table_id += (count - len(ipv6_table_id)) * [0]
96 if len(ipv4_table_id) < count:
97 ipv4_table_id += (count - len(ipv4_table_id)) * [0]
99 # create 'count' pg interfaces
100 self.create_pg_interfaces(range(count))
102 # setup all interfaces
103 for i in range(count):
104 intf = self.pg_interfaces[i]
105 self.configure_interface(
106 intf, ipv6[i], ipv4[i], ipv6_table_id[i], ipv4_table_id[i]
110 self.logger.debug(self.vapi.cli("show ip6 neighbors"))
112 self.logger.debug(self.vapi.cli("show ip4 neighbors"))
113 self.logger.debug(self.vapi.cli("show interface"))
114 self.logger.debug(self.vapi.cli("show hardware"))
116 return self.pg_interfaces
118 def teardown_interfaces(self):
119 """Unconfigure and bring down interface."""
120 self.logger.debug("Tearing down interfaces")
121 # tear down all interfaces
122 # AFAIK they cannot be deleted
123 for i in self.pg_interfaces:
124 self.logger.debug("Tear down interface %s" % (i.name))
130 def test_SRv6_End_AD_IPv6(self):
131 """Test SRv6 End.AD behavior with IPv6 traffic."""
132 self.src_addr = "a0::"
133 self.sid_list = ["a1::", "a2::a6", "a3::"]
134 self.test_sid_index = 1
136 # send traffic to one destination interface
137 # source and destination interfaces are IPv6 only
138 self.setup_interfaces(ipv6=[True, True])
140 # configure route to next segment
143 self.sid_list[self.test_sid_index + 1],
148 self.pg0.sw_if_index,
149 proto=DpoProto.DPO_PROTO_IP6,
153 route.add_vpp_config()
155 # configure SRv6 localSID behavior
157 "sr localsid address "
158 + self.sid_list[self.test_sid_index]
161 + self.pg1.remote_ip6
167 self.vapi.cli(cli_str)
170 self.logger.debug(self.vapi.cli("show sr localsid"))
172 # send one packet per packet size
173 count = len(self.pg_packet_sizes)
175 # prepare IPv6 in SRv6 headers
176 packet_header1 = self.create_packet_header_IPv6_SRH_IPv6(
177 srcaddr=self.src_addr,
178 sidlist=self.sid_list[::-1],
179 segleft=len(self.sid_list) - self.test_sid_index - 1,
182 # generate packets (pg0->pg1)
183 pkts1 = self.create_stream(
184 self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
187 # send packets and verify received packets
188 self.send_and_verify_pkts(
189 self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AD_IPv6_out
192 # log the localsid counters
193 self.logger.info(self.vapi.cli("show sr localsid"))
195 # prepare IPv6 header for returning packets
196 packet_header2 = self.create_packet_header_IPv6()
198 # generate returning packets (pg1->pg0)
199 pkts2 = self.create_stream(
200 self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
203 # send packets and verify received packets
204 self.send_and_verify_pkts(
205 self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AD_IPv6_in
208 # log the localsid counters
209 self.logger.info(self.vapi.cli("show sr localsid"))
211 # remove SRv6 localSIDs
212 cli_str = "sr localsid del address " + self.sid_list[self.test_sid_index]
213 self.vapi.cli(cli_str)
216 self.teardown_interfaces()
218 def compare_rx_tx_packet_End_AD_IPv6_out(self, tx_pkt, rx_pkt):
219 """Compare input and output packet after passing End.AD with IPv6
221 :param tx_pkt: transmitted packet
222 :param rx_pkt: received packet
225 # get first (outer) IPv6 header of rx'ed packet
226 rx_ip = rx_pkt.getlayer(IPv6)
228 tx_ip = tx_pkt.getlayer(IPv6)
229 tx_ip2 = tx_pkt.getlayer(IPv6, 2)
231 # verify if rx'ed packet has no SRH
232 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
234 # the whole rx_ip pkt should be equal to tx_ip2
235 # except for the hlim field
236 # -> adjust tx'ed hlim to expected hlim
237 tx_ip2.hlim = tx_ip2.hlim - 1
239 self.assertEqual(rx_ip, tx_ip2)
241 self.logger.debug("packet verification: SUCCESS")
243 def compare_rx_tx_packet_End_AD_IPv6_in(self, tx_pkt, rx_pkt):
244 """Compare input and output packet after passing End.AD
246 :param tx_pkt: transmitted packet
247 :param rx_pkt: received packet
250 # get first (outer) IPv6 header of rx'ed packet
251 rx_ip = rx_pkt.getlayer(IPv6)
252 # received ip.src should be equal to SR Policy source
253 self.assertEqual(rx_ip.src, self.src_addr)
254 # received ip.dst should be equal to expected sidlist next segment
255 self.assertEqual(rx_ip.dst, self.sid_list[self.test_sid_index + 1])
257 # rx'ed packet should have SRH
258 self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
261 rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
262 # rx'ed seglist should be equal to SID-list in reversed order
263 self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
264 # segleft should be equal to previous segleft value minus 1
265 self.assertEqual(rx_srh.segleft, len(self.sid_list) - self.test_sid_index - 2)
266 # lastentry should be equal to the SID-list length minus 1
267 self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
269 # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
270 # except for the hop-limit field
271 tx_ip = tx_pkt.getlayer(IPv6)
272 # -> update tx'ed hlim to the expected hlim
275 self.assertEqual(rx_srh.payload, tx_ip)
277 self.logger.debug("packet verification: SUCCESS")
279 def test_SRv6_End_AD_IPv4(self):
280 """Test SRv6 End.AD behavior with IPv4 traffic."""
281 self.src_addr = "a0::"
282 self.sid_list = ["a1::", "a2::a4", "a3::"]
283 self.test_sid_index = 1
285 # send traffic to one destination interface
286 # source and destination interfaces are IPv6 only
287 self.setup_interfaces(ipv6=[True, False], ipv4=[False, True])
289 # configure route to next segment
292 self.sid_list[self.test_sid_index + 1],
297 self.pg0.sw_if_index,
298 proto=DpoProto.DPO_PROTO_IP6,
302 route.add_vpp_config()
304 # configure SRv6 localSID behavior
306 "sr localsid address "
307 + self.sid_list[self.test_sid_index]
310 + self.pg1.remote_ip4
316 self.vapi.cli(cli_str)
319 self.logger.debug(self.vapi.cli("show sr localsid"))
321 # send one packet per packet size
322 count = len(self.pg_packet_sizes)
324 # prepare IPv4 in SRv6 headers
325 packet_header1 = self.create_packet_header_IPv6_SRH_IPv4(
326 srcaddr=self.src_addr,
327 sidlist=self.sid_list[::-1],
328 segleft=len(self.sid_list) - self.test_sid_index - 1,
331 # generate packets (pg0->pg1)
332 pkts1 = self.create_stream(
333 self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
336 # send packets and verify received packets
337 self.send_and_verify_pkts(
338 self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AD_IPv4_out
341 # log the localsid counters
342 self.logger.info(self.vapi.cli("show sr localsid"))
344 # prepare IPv6 header for returning packets
345 packet_header2 = self.create_packet_header_IPv4()
347 # generate returning packets (pg1->pg0)
348 pkts2 = self.create_stream(
349 self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
352 # send packets and verify received packets
353 self.send_and_verify_pkts(
354 self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AD_IPv4_in
357 # log the localsid counters
358 self.logger.info(self.vapi.cli("show sr localsid"))
360 # remove SRv6 localSIDs
361 cli_str = "sr localsid del address " + self.sid_list[self.test_sid_index]
362 self.vapi.cli(cli_str)
365 self.teardown_interfaces()
367 def compare_rx_tx_packet_End_AD_IPv4_out(self, tx_pkt, rx_pkt):
368 """Compare input and output packet after passing End.AD with IPv4
370 :param tx_pkt: transmitted packet
371 :param rx_pkt: received packet
374 # get IPv4 header of rx'ed packet
375 rx_ip = rx_pkt.getlayer(IP)
377 tx_ip = tx_pkt.getlayer(IPv6)
378 tx_ip2 = tx_pkt.getlayer(IP)
380 # verify if rx'ed packet has no SRH
381 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
383 # the whole rx_ip pkt should be equal to tx_ip2
384 # except for the ttl field and ip checksum
385 # -> adjust tx'ed ttl to expected ttl
386 tx_ip2.ttl = tx_ip2.ttl - 1
387 # -> set tx'ed ip checksum to None and let scapy recompute
389 # read back the pkt (with str()) to force computing these fields
390 # probably other ways to accomplish this are possible
391 tx_ip2 = IP(scapy.compat.raw(tx_ip2))
393 self.assertEqual(rx_ip, tx_ip2)
395 self.logger.debug("packet verification: SUCCESS")
397 def compare_rx_tx_packet_End_AD_IPv4_in(self, tx_pkt, rx_pkt):
398 """Compare input and output packet after passing End.AD
400 :param tx_pkt: transmitted packet
401 :param rx_pkt: received packet
404 # get first (outer) IPv6 header of rx'ed packet
405 rx_ip = rx_pkt.getlayer(IPv6)
406 # received ip.src should be equal to SR Policy source
407 self.assertEqual(rx_ip.src, self.src_addr)
408 # received ip.dst should be equal to expected sidlist next segment
409 self.assertEqual(rx_ip.dst, self.sid_list[self.test_sid_index + 1])
411 # rx'ed packet should have SRH
412 self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
415 rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
416 # rx'ed seglist should be equal to SID-list in reversed order
417 self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
418 # segleft should be equal to previous segleft value minus 1
419 self.assertEqual(rx_srh.segleft, len(self.sid_list) - self.test_sid_index - 2)
420 # lastentry should be equal to the SID-list length minus 1
421 self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
423 # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
424 # except for the ttl field and ip checksum
425 tx_ip = tx_pkt.getlayer(IP)
426 # -> adjust tx'ed ttl to expected ttl
427 tx_ip.ttl = tx_ip.ttl - 1
428 # -> set tx'ed ip checksum to None and let scapy recompute
430 # -> read back the pkt (with str()) to force computing these fields
431 # probably other ways to accomplish this are possible
432 self.assertEqual(rx_srh.payload, IP(scapy.compat.raw(tx_ip)))
434 self.logger.debug("packet verification: SUCCESS")
436 def test_SRv6_End_AD_L2(self):
437 """Test SRv6 End.AD behavior with L2 traffic."""
438 self.src_addr = "a0::"
439 self.sid_list = ["a1::", "a2::a4", "a3::"]
440 self.test_sid_index = 1
442 # send traffic to one destination interface
443 # source and destination interfaces are IPv6 only
444 self.setup_interfaces(ipv6=[True, False])
446 # configure route to next segment
449 self.sid_list[self.test_sid_index + 1],
454 self.pg0.sw_if_index,
455 proto=DpoProto.DPO_PROTO_IP6,
459 route.add_vpp_config()
461 # configure SRv6 localSID behavior
463 "sr localsid address "
464 + self.sid_list[self.test_sid_index]
471 self.vapi.cli(cli_str)
474 self.logger.debug(self.vapi.cli("show sr localsid"))
476 # send one packet per packet size
477 count = len(self.pg_packet_sizes)
479 # prepare L2 in SRv6 headers
480 packet_header1 = self.create_packet_header_IPv6_SRH_L2(
481 srcaddr=self.src_addr,
482 sidlist=self.sid_list[::-1],
483 segleft=len(self.sid_list) - self.test_sid_index - 1,
487 # generate packets (pg0->pg1)
488 pkts1 = self.create_stream(
489 self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
492 # send packets and verify received packets
493 self.send_and_verify_pkts(
494 self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AD_L2_out
497 # log the localsid counters
498 self.logger.info(self.vapi.cli("show sr localsid"))
500 # prepare L2 header for returning packets
501 packet_header2 = self.create_packet_header_L2()
503 # generate returning packets (pg1->pg0)
504 pkts2 = self.create_stream(
505 self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
508 # send packets and verify received packets
509 self.send_and_verify_pkts(
510 self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AD_L2_in
513 # log the localsid counters
514 self.logger.info(self.vapi.cli("show sr localsid"))
516 # remove SRv6 localSIDs
517 cli_str = "sr localsid del address " + self.sid_list[self.test_sid_index]
518 self.vapi.cli(cli_str)
521 self.teardown_interfaces()
523 def compare_rx_tx_packet_End_AD_L2_out(self, tx_pkt, rx_pkt):
524 """Compare input and output packet after passing End.AD with L2
526 :param tx_pkt: transmitted packet
527 :param rx_pkt: received packet
530 # get IPv4 header of rx'ed packet
531 rx_eth = rx_pkt.getlayer(Ether)
533 tx_ip = tx_pkt.getlayer(IPv6)
534 # we can't just get the 2nd Ether layer
535 # get the Raw content and dissect it as Ether
536 tx_eth1 = Ether(scapy.compat.raw(tx_pkt[Raw]))
538 # verify if rx'ed packet has no SRH
539 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
541 # the whole rx_eth pkt should be equal to tx_eth1
542 self.assertEqual(rx_eth, tx_eth1)
544 self.logger.debug("packet verification: SUCCESS")
546 def compare_rx_tx_packet_End_AD_L2_in(self, tx_pkt, rx_pkt):
547 """Compare input and output packet after passing End.AD
549 :param tx_pkt: transmitted packet
550 :param rx_pkt: received packet
554 # get first (outer) IPv6 header of rx'ed packet
555 rx_ip = rx_pkt.getlayer(IPv6)
556 # received ip.src should be equal to SR Policy source
557 self.assertEqual(rx_ip.src, self.src_addr)
558 # received ip.dst should be equal to expected sidlist next segment
559 self.assertEqual(rx_ip.dst, self.sid_list[self.test_sid_index + 1])
561 # rx'ed packet should have SRH
562 self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
565 rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
566 # rx'ed seglist should be equal to SID-list in reversed order
567 self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
568 # segleft should be equal to previous segleft value minus 1
569 self.assertEqual(rx_srh.segleft, len(self.sid_list) - self.test_sid_index - 2)
570 # lastentry should be equal to the SID-list length minus 1
571 self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
573 # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
574 tx_ether = tx_pkt.getlayer(Ether)
575 self.assertEqual(Ether(scapy.compat.raw(rx_srh.payload)), tx_ether)
577 self.logger.debug("packet verification: SUCCESS")
579 def create_stream(self, src_if, dst_if, packet_header, packet_sizes, count):
580 """Create SRv6 input packet stream for defined interface.
582 :param VppInterface src_if: Interface to create packet stream for
583 :param VppInterface dst_if: destination interface of packet stream
584 :param packet_header: Layer3 scapy packet headers,
585 L2 is added when not provided,
586 Raw(payload) with packet_info is added
587 :param list packet_sizes: packet stream pckt sizes,sequentially applied
588 to packets in stream have
589 :param int count: number of packets in packet stream
590 :return: list of packets
592 self.logger.info("Creating packets")
594 for i in range(0, count - 1):
595 payload_info = self.create_packet_info(src_if, dst_if)
596 self.logger.debug("Creating packet with index %d" % (payload_info.index))
597 payload = self.info_to_payload(payload_info)
598 # add L2 header if not yet provided in packet_header
599 if packet_header.getlayer(0).name == "Ethernet":
600 p = packet_header / Raw(payload)
603 Ether(dst=src_if.local_mac, src=src_if.remote_mac)
607 size = packet_sizes[i % len(packet_sizes)]
608 self.logger.debug("Packet size %d" % (size))
609 self.extend_packet(p, size)
610 # we need to store the packet with the automatic fields computed
611 # read back the dumped packet (with str())
612 # to force computing these fields
613 # probably other ways are possible
614 p = Ether(scapy.compat.raw(p))
615 payload_info.data = p.copy()
616 self.logger.debug(ppp("Created packet:", p))
618 self.logger.info("Done creating packets")
621 def send_and_verify_pkts(self, input, pkts, output, compare_func):
622 """Send packets and verify received packets using compare_func
624 :param input: ingress interface of DUT
625 :param pkts: list of packets to transmit
626 :param output: egress interface of DUT
627 :param compare_func: function to compare in and out packets
629 # add traffic stream to input interface
630 input.add_stream(pkts)
632 # enable capture on all interfaces
633 self.pg_enable_capture(self.pg_interfaces)
636 self.logger.info("Starting traffic")
640 self.logger.info("Getting packet capture")
641 capture = output.get_capture()
643 # assert nothing was captured on input interface
644 # input.assert_nothing_captured()
646 # verify captured packets
647 self.verify_captured_pkts(output, capture, compare_func)
649 def create_packet_header_IPv6(self):
650 """Create packet header: IPv6 header, UDP header
652 :param dst: IPv6 destination address
654 IPv6 source address is 1234::1
655 IPv6 destination address is 4321::1
656 UDP source port and destination port are 1234
659 p = IPv6(src="1234::1", dst="4321::1") / UDP(sport=1234, dport=1234)
662 def create_packet_header_IPv6_SRH_IPv6(self, srcaddr, sidlist, segleft):
663 """Create packet header: IPv6 encapsulated in SRv6:
664 IPv6 header with SRH, IPv6 header, UDP header
666 :param int srcaddr: outer source address
667 :param list sidlist: segment list of outer IPv6 SRH
668 :param int segleft: segments-left field of outer IPv6 SRH
670 Outer IPv6 source address is set to srcaddr
671 Outer IPv6 destination address is set to sidlist[segleft]
672 Inner IPv6 source addresses is 1234::1
673 Inner IPv6 destination address is 4321::1
674 UDP source port and destination port are 1234
678 IPv6(src=srcaddr, dst=sidlist[segleft])
679 / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=41)
680 / IPv6(src="1234::1", dst="4321::1")
681 / UDP(sport=1234, dport=1234)
685 def create_packet_header_IPv4(self):
686 """Create packet header: IPv4 header, UDP header
688 :param dst: IPv4 destination address
690 IPv4 source address is 123.1.1.1
691 IPv4 destination address is 124.1.1.1
692 UDP source port and destination port are 1234
695 p = IP(src="123.1.1.1", dst="124.1.1.1") / UDP(sport=1234, dport=1234)
698 def create_packet_header_IPv6_SRH_IPv4(self, srcaddr, sidlist, segleft):
699 """Create packet header: IPv4 encapsulated in SRv6:
700 IPv6 header with SRH, IPv4 header, UDP header
702 :param int srcaddr: outer source address
703 :param list sidlist: segment list of outer IPv6 SRH
704 :param int segleft: segments-left field of outer IPv6 SRH
706 Outer IPv6 source address is set to srcaddr
707 Outer IPv6 destination address is set to sidlist[segleft]
708 Inner IPv4 source address is 123.1.1.1
709 Inner IPv4 destination address is 124.1.1.1
710 UDP source port and destination port are 1234
714 IPv6(src=srcaddr, dst=sidlist[segleft])
715 / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=4)
716 / IP(src="123.1.1.1", dst="124.1.1.1")
717 / UDP(sport=1234, dport=1234)
721 def create_packet_header_L2(self, vlan=0):
722 """Create packet header: L2 header
724 :param vlan: if vlan!=0 then add 802.1q header
726 # Note: the dst addr ('00:55:44:33:22:11') is used in
727 # the compare function compare_rx_tx_packet_T_Encaps_L2
728 # to detect presence of L2 in SRH payload
729 p = Ether(src="00:11:22:33:44:55", dst="00:55:44:33:22:11")
733 p /= Dot1Q(vlan=vlan, type=etype)
738 def create_packet_header_IPv6_SRH_L2(self, srcaddr, sidlist, segleft, vlan=0):
739 """Create packet header: L2 encapsulated in SRv6:
740 IPv6 header with SRH, L2
742 :param int srcaddr: IPv6 source address
743 :param list sidlist: segment list of outer IPv6 SRH
744 :param int segleft: segments-left field of outer IPv6 SRH
745 :param vlan: L2 vlan; if vlan!=0 then add 802.1q header
747 IPv6 source address is set to srcaddr
748 IPv6 destination address is set to sidlist[segleft]
750 eth = Ether(src="00:11:22:33:44:55", dst="00:55:44:33:22:11")
754 eth /= Dot1Q(vlan=vlan, type=etype)
759 IPv6(src=srcaddr, dst=sidlist[segleft])
760 / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=143)
765 def get_payload_info(self, packet):
766 """Extract the payload_info from the packet"""
767 # in most cases, payload_info is in packet[Raw]
768 # but packet[Raw] gives the complete payload
769 # (incl L2 header) for the T.Encaps L2 case
771 payload_info = self.payload_to_info(packet[Raw])
774 # remote L2 header from packet[Raw]:
775 # take packet[Raw], convert it to an Ether layer
776 # and then extract Raw from it
777 payload_info = self.payload_to_info(
778 Ether(scapy.compat.raw(packet[Raw]))[Raw]
783 def verify_captured_pkts(self, dst_if, capture, compare_func):
785 Verify captured packet stream for specified interface.
786 Compare ingress with egress packets using the specified compare fn
788 :param dst_if: egress interface of DUT
789 :param capture: captured packets
790 :param compare_func: function to compare in and out packet
793 "Verifying capture on interface %s using function %s"
794 % (dst_if.name, compare_func.__name__)
798 for i in self.pg_interfaces:
799 last_info[i.sw_if_index] = None
800 dst_sw_if_index = dst_if.sw_if_index
802 for packet in capture:
804 # extract payload_info from packet's payload
805 payload_info = self.get_payload_info(packet)
806 packet_index = payload_info.index
808 self.logger.debug("Verifying packet with index %d" % (packet_index))
809 # packet should have arrived on the expected interface
810 self.assertEqual(payload_info.dst, dst_sw_if_index)
812 "Got packet on interface %s: src=%u (idx=%u)"
813 % (dst_if.name, payload_info.src, packet_index)
816 # search for payload_info with same src and dst if_index
817 # this will give us the transmitted packet
818 next_info = self.get_next_packet_info_for_interface2(
819 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
821 last_info[payload_info.src] = next_info
822 # next_info should not be None
823 self.assertTrue(next_info is not None)
824 # index of tx and rx packets should be equal
825 self.assertEqual(packet_index, next_info.index)
826 # data field of next_info contains the tx packet
827 txed_packet = next_info.data
830 ppp("Transmitted packet:", txed_packet)
831 ) # ppp=Pretty Print Packet
833 self.logger.debug(ppp("Received packet:", packet))
835 # compare rcvd packet with expected packet using compare_func
836 compare_func(txed_packet, packet)
839 self.logger.error(ppp("Unexpected or invalid packet:", packet))
842 # have all expected packets arrived?
843 for i in self.pg_interfaces:
844 remaining_packet = self.get_next_packet_info_for_interface2(
845 i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index]
848 remaining_packet is None,
849 "Interface %s: Packet expected from interface %s "
850 "didn't arrive" % (dst_if.name, i.name),
854 if __name__ == "__main__":
855 unittest.main(testRunner=VppTestRunner)