6 from framework import VppTestCase, VppTestRunner
7 from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \
8 VppMplsIpBind, VppIpMRoute, VppMRoutePath, \
9 MRouteItfFlags, MRouteEntryFlags
10 from vpp_mpls_tunnel_interface import VppMPLSTunnelInterface
12 from scapy.packet import Raw
13 from scapy.layers.l2 import Ether
14 from scapy.layers.inet import IP, UDP, ICMP
15 from scapy.layers.inet6 import IPv6
16 from scapy.contrib.mpls import MPLS
19 class TestMPLS(VppTestCase):
20 """ MPLS Test Case """
23 super(TestMPLS, self).setUp()
25 # create 2 pg interfaces
26 self.create_pg_interfaces(range(4))
28 # setup both interfaces
29 # assign them different tables.
32 for i in self.pg_interfaces:
34 i.set_table_ip4(table_id)
35 i.set_table_ip6(table_id)
44 super(TestMPLS, self).tearDown()
45 for i in self.pg_interfaces:
51 # the default of 64 matches the IP packet TTL default
52 def create_stream_labelled_ip4(
61 self.reset_packet_infos()
64 info = self.create_packet_info(src_if, src_if)
65 payload = self.info_to_payload(info)
66 p = Ether(dst=src_if.local_mac, src=src_if.remote_mac)
68 for ii in range(len(mpls_labels)):
69 if ii == len(mpls_labels) - 1:
70 p = p / MPLS(label=mpls_labels[ii], ttl=mpls_ttl, s=1)
72 p = p / MPLS(label=mpls_labels[ii], ttl=mpls_ttl, s=0)
75 p = (p / IP(src=src_if.local_ip4, dst=src_if.remote_ip4) /
76 UDP(sport=1234, dport=1234) /
79 p = (p / IP(src=src_if.local_ip4, dst=dst_ip) /
80 UDP(sport=1234, dport=1234) /
83 p = (p / IP(src=ip_itf.remote_ip4,
84 dst=ip_itf.local_ip4) /
91 def create_stream_ip4(self, src_if, dst_ip):
92 self.reset_packet_infos()
94 for i in range(0, 257):
95 info = self.create_packet_info(src_if, src_if)
96 payload = self.info_to_payload(info)
97 p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
98 IP(src=src_if.remote_ip4, dst=dst_ip) /
99 UDP(sport=1234, dport=1234) /
105 def create_stream_labelled_ip6(self, src_if, mpls_label, mpls_ttl,
108 dst_ip = src_if.remote_ip6
109 self.reset_packet_infos()
111 for i in range(0, 257):
112 info = self.create_packet_info(src_if, src_if)
113 payload = self.info_to_payload(info)
114 p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
115 MPLS(label=mpls_label, ttl=mpls_ttl) /
116 IPv6(src=src_if.remote_ip6, dst=dst_ip) /
117 UDP(sport=1234, dport=1234) /
124 def verify_filter(capture, sent):
125 if not len(capture) == len(sent):
126 # filter out any IPv6 RAs from the capture
132 def verify_capture_ip4(self, src_if, capture, sent, ping_resp=0):
134 capture = self.verify_filter(capture, sent)
136 self.assertEqual(len(capture), len(sent))
138 for i in range(len(capture)):
142 # the rx'd packet has the MPLS label popped
144 self.assertEqual(eth.type, 0x800)
150 self.assertEqual(rx_ip.src, tx_ip.src)
151 self.assertEqual(rx_ip.dst, tx_ip.dst)
152 # IP processing post pop has decremented the TTL
153 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
155 self.assertEqual(rx_ip.src, tx_ip.dst)
156 self.assertEqual(rx_ip.dst, tx_ip.src)
161 def verify_mpls_stack(self, rx, mpls_labels, ttl=255, num=0):
162 # the rx'd packet has the MPLS label popped
164 self.assertEqual(eth.type, 0x8847)
168 for ii in range(len(mpls_labels)):
169 self.assertEqual(rx_mpls.label, mpls_labels[ii])
170 self.assertEqual(rx_mpls.cos, 0)
172 self.assertEqual(rx_mpls.ttl, ttl)
174 self.assertEqual(rx_mpls.ttl, 255)
176 if ii == len(mpls_labels) - 1:
177 self.assertEqual(rx_mpls.s, 1)
180 self.assertEqual(rx_mpls.s, 0)
181 # pop the label to expose the next
182 rx_mpls = rx_mpls[MPLS].payload
184 def verify_capture_labelled_ip4(self, src_if, capture, sent,
187 capture = self.verify_filter(capture, sent)
189 self.assertEqual(len(capture), len(sent))
191 for i in range(len(capture)):
197 # the MPLS TTL is copied from the IP
198 self.verify_mpls_stack(
199 rx, mpls_labels, rx_ip.ttl, len(mpls_labels) - 1)
201 self.assertEqual(rx_ip.src, tx_ip.src)
202 self.assertEqual(rx_ip.dst, tx_ip.dst)
203 # IP processing post pop has decremented the TTL
204 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
209 def verify_capture_tunneled_ip4(self, src_if, capture, sent, mpls_labels,
212 top = len(mpls_labels) - 1
214 capture = self.verify_filter(capture, sent)
216 self.assertEqual(len(capture), len(sent))
218 for i in range(len(capture)):
224 # the MPLS TTL is 255 since it enters a new tunnel
225 self.verify_mpls_stack(
226 rx, mpls_labels, ttl, top)
228 self.assertEqual(rx_ip.src, tx_ip.src)
229 self.assertEqual(rx_ip.dst, tx_ip.dst)
230 # IP processing post pop has decremented the TTL
231 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
236 def verify_capture_labelled(self, src_if, capture, sent,
237 mpls_labels, ttl=254, num=0):
239 capture = self.verify_filter(capture, sent)
241 self.assertEqual(len(capture), len(sent))
243 for i in range(len(capture)):
245 self.verify_mpls_stack(rx, mpls_labels, ttl, num)
249 def verify_capture_ip6(self, src_if, capture, sent):
251 self.assertEqual(len(capture), len(sent))
253 for i in range(len(capture)):
257 # the rx'd packet has the MPLS label popped
259 self.assertEqual(eth.type, 0x86DD)
264 self.assertEqual(rx_ip.src, tx_ip.src)
265 self.assertEqual(rx_ip.dst, tx_ip.dst)
266 # IP processing post pop has decremented the TTL
267 self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)
272 def send_and_assert_no_replies(self, intf, pkts, remark):
273 intf.add_stream(pkts)
274 self.pg_enable_capture(self.pg_interfaces)
276 for i in self.pg_interfaces:
277 i.assert_nothing_captured(remark=remark)
280 """ MPLS label swap tests """
283 # A simple MPLS xconnect - eos label in label out
285 route_32_eos = VppMplsRoute(self, 32, 1,
286 [VppRoutePath(self.pg0.remote_ip4,
287 self.pg0.sw_if_index,
289 route_32_eos.add_vpp_config()
292 # a stream that matches the route for 10.0.0.1
293 # PG0 is in the default table
295 self.vapi.cli("clear trace")
296 tx = self.create_stream_labelled_ip4(self.pg0, [32])
297 self.pg0.add_stream(tx)
299 self.pg_enable_capture(self.pg_interfaces)
302 rx = self.pg0.get_capture()
303 self.verify_capture_labelled(self.pg0, rx, tx, [33])
306 # A simple MPLS xconnect - non-eos label in label out
308 route_32_neos = VppMplsRoute(self, 32, 0,
309 [VppRoutePath(self.pg0.remote_ip4,
310 self.pg0.sw_if_index,
312 route_32_neos.add_vpp_config()
315 # a stream that matches the route for 10.0.0.1
316 # PG0 is in the default table
318 self.vapi.cli("clear trace")
319 tx = self.create_stream_labelled_ip4(self.pg0, [32, 99])
320 self.pg0.add_stream(tx)
322 self.pg_enable_capture(self.pg_interfaces)
325 rx = self.pg0.get_capture()
326 self.verify_capture_labelled(self.pg0, rx, tx, [33, 99])
329 # An MPLS xconnect - EOS label in IP out
331 route_33_eos = VppMplsRoute(self, 33, 1,
332 [VppRoutePath(self.pg0.remote_ip4,
333 self.pg0.sw_if_index,
335 route_33_eos.add_vpp_config()
337 self.vapi.cli("clear trace")
338 tx = self.create_stream_labelled_ip4(self.pg0, [33])
339 self.pg0.add_stream(tx)
341 self.pg_enable_capture(self.pg_interfaces)
344 rx = self.pg0.get_capture()
345 self.verify_capture_ip4(self.pg0, rx, tx)
348 # An MPLS xconnect - non-EOS label in IP out - an invalid configuration
349 # so this traffic should be dropped.
351 route_33_neos = VppMplsRoute(self, 33, 0,
352 [VppRoutePath(self.pg0.remote_ip4,
353 self.pg0.sw_if_index,
355 route_33_neos.add_vpp_config()
357 self.vapi.cli("clear trace")
358 tx = self.create_stream_labelled_ip4(self.pg0, [33, 99])
359 self.pg0.add_stream(tx)
361 self.pg_enable_capture(self.pg_interfaces)
363 self.pg0.assert_nothing_captured(
364 remark="MPLS non-EOS packets popped and forwarded")
367 # A recursive EOS x-connect, which resolves through another x-connect
369 route_34_eos = VppMplsRoute(self, 34, 1,
370 [VppRoutePath("0.0.0.0",
374 route_34_eos.add_vpp_config()
376 tx = self.create_stream_labelled_ip4(self.pg0, [34])
377 self.pg0.add_stream(tx)
379 self.pg_enable_capture(self.pg_interfaces)
382 rx = self.pg0.get_capture()
383 self.verify_capture_labelled(self.pg0, rx, tx, [33, 44, 45], num=2)
386 # A recursive non-EOS x-connect, which resolves through another
389 route_34_neos = VppMplsRoute(self, 34, 0,
390 [VppRoutePath("0.0.0.0",
394 route_34_neos.add_vpp_config()
396 self.vapi.cli("clear trace")
397 tx = self.create_stream_labelled_ip4(self.pg0, [34, 99])
398 self.pg0.add_stream(tx)
400 self.pg_enable_capture(self.pg_interfaces)
403 rx = self.pg0.get_capture()
404 # it's the 2nd (counting from 0) label in the stack that is swapped
405 self.verify_capture_labelled(self.pg0, rx, tx, [33, 44, 46, 99], num=2)
408 # an recursive IP route that resolves through the recursive non-eos
411 ip_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
412 [VppRoutePath("0.0.0.0",
416 ip_10_0_0_1.add_vpp_config()
418 self.vapi.cli("clear trace")
419 tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
420 self.pg0.add_stream(tx)
422 self.pg_enable_capture(self.pg_interfaces)
425 rx = self.pg0.get_capture()
426 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [33, 44, 46, 55])
428 ip_10_0_0_1.remove_vpp_config()
429 route_34_neos.remove_vpp_config()
430 route_34_eos.remove_vpp_config()
431 route_33_neos.remove_vpp_config()
432 route_33_eos.remove_vpp_config()
433 route_32_neos.remove_vpp_config()
434 route_32_eos.remove_vpp_config()
437 """ MPLS Local Label Binding test """
440 # Add a non-recursive route with a single out label
442 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
443 [VppRoutePath(self.pg0.remote_ip4,
444 self.pg0.sw_if_index,
446 route_10_0_0_1.add_vpp_config()
448 # bind a local label to the route
449 binding = VppMplsIpBind(self, 44, "10.0.0.1", 32)
450 binding.add_vpp_config()
453 self.vapi.cli("clear trace")
454 tx = self.create_stream_labelled_ip4(self.pg0, [44, 99])
455 self.pg0.add_stream(tx)
457 self.pg_enable_capture(self.pg_interfaces)
460 rx = self.pg0.get_capture()
461 self.verify_capture_labelled(self.pg0, rx, tx, [45, 99])
464 self.vapi.cli("clear trace")
465 tx = self.create_stream_labelled_ip4(self.pg0, [44])
466 self.pg0.add_stream(tx)
468 self.pg_enable_capture(self.pg_interfaces)
471 rx = self.pg0.get_capture()
472 self.verify_capture_labelled(self.pg0, rx, tx, [45])
475 self.vapi.cli("clear trace")
476 tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
477 self.pg0.add_stream(tx)
479 self.pg_enable_capture(self.pg_interfaces)
482 rx = self.pg0.get_capture()
483 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [45])
488 binding.remove_vpp_config()
489 route_10_0_0_1.remove_vpp_config()
491 def test_imposition(self):
492 """ MPLS label imposition test """
495 # Add a non-recursive route with a single out label
497 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
498 [VppRoutePath(self.pg0.remote_ip4,
499 self.pg0.sw_if_index,
501 route_10_0_0_1.add_vpp_config()
504 # a stream that matches the route for 10.0.0.1
505 # PG0 is in the default table
507 self.vapi.cli("clear trace")
508 tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
509 self.pg0.add_stream(tx)
511 self.pg_enable_capture(self.pg_interfaces)
514 rx = self.pg0.get_capture()
515 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32])
518 # Add a non-recursive route with a 3 out labels
520 route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
521 [VppRoutePath(self.pg0.remote_ip4,
522 self.pg0.sw_if_index,
523 labels=[32, 33, 34])])
524 route_10_0_0_2.add_vpp_config()
527 # a stream that matches the route for 10.0.0.1
528 # PG0 is in the default table
530 self.vapi.cli("clear trace")
531 tx = self.create_stream_ip4(self.pg0, "10.0.0.2")
532 self.pg0.add_stream(tx)
534 self.pg_enable_capture(self.pg_interfaces)
537 rx = self.pg0.get_capture()
538 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32, 33, 34])
541 # add a recursive path, with output label, via the 1 label route
543 route_11_0_0_1 = VppIpRoute(self, "11.0.0.1", 32,
544 [VppRoutePath("10.0.0.1",
547 route_11_0_0_1.add_vpp_config()
550 # a stream that matches the route for 11.0.0.1, should pick up
551 # the label stack for 11.0.0.1 and 10.0.0.1
553 self.vapi.cli("clear trace")
554 tx = self.create_stream_ip4(self.pg0, "11.0.0.1")
555 self.pg0.add_stream(tx)
557 self.pg_enable_capture(self.pg_interfaces)
560 rx = self.pg0.get_capture()
561 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32, 44])
564 # add a recursive path, with 2 labels, via the 3 label route
566 route_11_0_0_2 = VppIpRoute(self, "11.0.0.2", 32,
567 [VppRoutePath("10.0.0.2",
570 route_11_0_0_2.add_vpp_config()
573 # a stream that matches the route for 11.0.0.1, should pick up
574 # the label stack for 11.0.0.1 and 10.0.0.1
576 self.vapi.cli("clear trace")
577 tx = self.create_stream_ip4(self.pg0, "11.0.0.2")
578 self.pg0.add_stream(tx)
580 self.pg_enable_capture(self.pg_interfaces)
583 rx = self.pg0.get_capture()
584 self.verify_capture_labelled_ip4(
585 self.pg0, rx, tx, [32, 33, 34, 44, 45])
590 route_11_0_0_2.remove_vpp_config()
591 route_11_0_0_1.remove_vpp_config()
592 route_10_0_0_2.remove_vpp_config()
593 route_10_0_0_1.remove_vpp_config()
595 def test_tunnel(self):
596 """ MPLS Tunnel Tests """
599 # Create a tunnel with a single out label
601 mpls_tun = VppMPLSTunnelInterface(self,
602 [VppRoutePath(self.pg0.remote_ip4,
603 self.pg0.sw_if_index,
605 mpls_tun.add_vpp_config()
609 # add an unlabelled route through the new tunnel
611 route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
612 [VppRoutePath("0.0.0.0",
613 mpls_tun._sw_if_index)])
614 route_10_0_0_3.add_vpp_config()
616 self.vapi.cli("clear trace")
617 tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
618 self.pg0.add_stream(tx)
620 self.pg_enable_capture(self.pg_interfaces)
623 rx = self.pg0.get_capture()
624 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [44, 46])
627 # add a labelled route through the new tunnel
629 route_10_0_0_4 = VppIpRoute(self, "10.0.0.4", 32,
630 [VppRoutePath("0.0.0.0",
631 mpls_tun._sw_if_index,
633 route_10_0_0_4.add_vpp_config()
635 self.vapi.cli("clear trace")
636 tx = self.create_stream_ip4(self.pg0, "10.0.0.4")
637 self.pg0.add_stream(tx)
639 self.pg_enable_capture(self.pg_interfaces)
642 rx = self.pg0.get_capture()
643 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [44, 46, 33],
646 def test_v4_exp_null(self):
647 """ MPLS V4 Explicit NULL test """
650 # The first test case has an MPLS TTL of 0
651 # all packet should be dropped
653 tx = self.create_stream_labelled_ip4(self.pg0, [0], 0)
654 self.pg0.add_stream(tx)
656 self.pg_enable_capture(self.pg_interfaces)
659 self.pg0.assert_nothing_captured(remark="MPLS TTL=0 packets forwarded")
662 # a stream with a non-zero MPLS TTL
663 # PG0 is in the default table
665 tx = self.create_stream_labelled_ip4(self.pg0, [0])
666 self.pg0.add_stream(tx)
668 self.pg_enable_capture(self.pg_interfaces)
671 rx = self.pg0.get_capture()
672 self.verify_capture_ip4(self.pg0, rx, tx)
675 # a stream with a non-zero MPLS TTL
677 # we are ensuring the post-pop lookup occurs in the VRF table
679 self.vapi.cli("clear trace")
680 tx = self.create_stream_labelled_ip4(self.pg1, [0])
681 self.pg1.add_stream(tx)
683 self.pg_enable_capture(self.pg_interfaces)
686 rx = self.pg1.get_capture()
687 self.verify_capture_ip4(self.pg0, rx, tx)
689 def test_v6_exp_null(self):
690 """ MPLS V6 Explicit NULL test """
693 # a stream with a non-zero MPLS TTL
694 # PG0 is in the default table
696 self.vapi.cli("clear trace")
697 tx = self.create_stream_labelled_ip6(self.pg0, 2, 2)
698 self.pg0.add_stream(tx)
700 self.pg_enable_capture(self.pg_interfaces)
703 rx = self.pg0.get_capture()
704 self.verify_capture_ip6(self.pg0, rx, tx)
707 # a stream with a non-zero MPLS TTL
709 # we are ensuring the post-pop lookup occurs in the VRF table
711 self.vapi.cli("clear trace")
712 tx = self.create_stream_labelled_ip6(self.pg1, 2, 2)
713 self.pg1.add_stream(tx)
715 self.pg_enable_capture(self.pg_interfaces)
718 rx = self.pg1.get_capture()
719 self.verify_capture_ip6(self.pg0, rx, tx)
725 # A de-agg route - next-hop lookup in default table
727 route_34_eos = VppMplsRoute(self, 34, 1,
728 [VppRoutePath("0.0.0.0",
731 route_34_eos.add_vpp_config()
734 # ping an interface in the default table
735 # PG0 is in the default table
737 self.vapi.cli("clear trace")
738 tx = self.create_stream_labelled_ip4(self.pg0, [34], ping=1,
740 self.pg0.add_stream(tx)
742 self.pg_enable_capture(self.pg_interfaces)
745 rx = self.pg0.get_capture()
746 self.verify_capture_ip4(self.pg0, rx, tx, ping_resp=1)
749 # A de-agg route - next-hop lookup in non-default table
751 route_35_eos = VppMplsRoute(self, 35, 1,
752 [VppRoutePath("0.0.0.0",
755 route_35_eos.add_vpp_config()
758 # ping an interface in the non-default table
759 # PG0 is in the default table. packet arrive labelled in the
760 # default table and egress unlabelled in the non-default
762 self.vapi.cli("clear trace")
763 tx = self.create_stream_labelled_ip4(
764 self.pg0, [35], ping=1, ip_itf=self.pg1)
765 self.pg0.add_stream(tx)
767 self.pg_enable_capture(self.pg_interfaces)
770 packet_count = self.get_packet_count_for_if_idx(self.pg0.sw_if_index)
771 rx = self.pg1.get_capture(packet_count)
772 self.verify_capture_ip4(self.pg1, rx, tx, ping_resp=1)
777 route_36_neos = VppMplsRoute(self, 36, 0,
778 [VppRoutePath("0.0.0.0",
780 route_36_neos.add_vpp_config()
782 self.vapi.cli("clear trace")
783 tx = self.create_stream_labelled_ip4(self.pg0, [36, 35],
784 ping=1, ip_itf=self.pg1)
785 self.pg0.add_stream(tx)
787 self.pg_enable_capture(self.pg_interfaces)
790 rx = self.pg1.get_capture(len(tx))
791 self.verify_capture_ip4(self.pg1, rx, tx, ping_resp=1)
793 route_36_neos.remove_vpp_config()
794 route_35_eos.remove_vpp_config()
795 route_34_eos.remove_vpp_config()
797 def test_interface_rx(self):
798 """ MPLS Interface Receive """
801 # Add a non-recursive route that will forward the traffic
804 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
806 paths=[VppRoutePath(self.pg1.remote_ip4,
807 self.pg1.sw_if_index)])
808 route_10_0_0_1.add_vpp_config()
811 # An interface receive label that maps traffic to RX on interface
813 # by injecting the packet in on pg0, which is in table 0
814 # doing an interface-rx on pg1 and matching a route in table 1
815 # if the packet egresses, then we must have swapped to pg1
816 # so as to have matched the route in table 1
818 route_34_eos = VppMplsRoute(self, 34, 1,
819 [VppRoutePath("0.0.0.0",
820 self.pg1.sw_if_index,
822 route_34_eos.add_vpp_config()
825 # ping an interface in the default table
826 # PG0 is in the default table
828 self.vapi.cli("clear trace")
829 tx = self.create_stream_labelled_ip4(self.pg0, [34], n=257,
831 self.pg0.add_stream(tx)
833 self.pg_enable_capture(self.pg_interfaces)
836 rx = self.pg1.get_capture(257)
837 self.verify_capture_ip4(self.pg1, rx, tx)
839 def test_mcast_mid_point(self):
840 """ MPLS Multicast Mid Point """
843 # Add a non-recursive route that will forward the traffic
846 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
848 paths=[VppRoutePath(self.pg1.remote_ip4,
849 self.pg1.sw_if_index)])
850 route_10_0_0_1.add_vpp_config()
853 # Add a mcast entry that replicate to pg2 and pg3
854 # and replicate to a interface-rx (like a bud node would)
856 route_3400_eos = VppMplsRoute(self, 3400, 1,
857 [VppRoutePath(self.pg2.remote_ip4,
858 self.pg2.sw_if_index,
860 VppRoutePath(self.pg3.remote_ip4,
861 self.pg3.sw_if_index,
863 VppRoutePath("0.0.0.0",
864 self.pg1.sw_if_index,
867 route_3400_eos.add_vpp_config()
870 # ping an interface in the default table
871 # PG0 is in the default table
873 self.vapi.cli("clear trace")
874 tx = self.create_stream_labelled_ip4(self.pg0, [3400], n=257,
876 self.pg0.add_stream(tx)
878 self.pg_enable_capture(self.pg_interfaces)
881 rx = self.pg1.get_capture(257)
882 self.verify_capture_ip4(self.pg1, rx, tx)
884 rx = self.pg2.get_capture(257)
885 self.verify_capture_labelled(self.pg2, rx, tx, [3401])
886 rx = self.pg3.get_capture(257)
887 self.verify_capture_labelled(self.pg3, rx, tx, [3402])
889 def test_mcast_head(self):
890 """ MPLS Multicast Head-end """
893 # Create a multicast tunnel with two replications
895 mpls_tun = VppMPLSTunnelInterface(self,
896 [VppRoutePath(self.pg2.remote_ip4,
897 self.pg2.sw_if_index,
899 VppRoutePath(self.pg3.remote_ip4,
900 self.pg3.sw_if_index,
903 mpls_tun.add_vpp_config()
907 # add an unlabelled route through the new tunnel
909 route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
910 [VppRoutePath("0.0.0.0",
911 mpls_tun._sw_if_index)])
912 route_10_0_0_3.add_vpp_config()
914 self.vapi.cli("clear trace")
915 tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
916 self.pg0.add_stream(tx)
918 self.pg_enable_capture(self.pg_interfaces)
921 rx = self.pg2.get_capture(257)
922 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [42])
923 rx = self.pg3.get_capture(257)
924 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [43])
927 # An an IP multicast route via the tunnel
929 # one accepting interface, pg0, 1 forwarding interface via the tunnel
931 route_232_1_1_1 = VppIpMRoute(
935 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
936 [VppMRoutePath(self.pg0.sw_if_index,
937 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
938 VppMRoutePath(mpls_tun._sw_if_index,
939 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
940 route_232_1_1_1.add_vpp_config()
942 self.vapi.cli("clear trace")
943 tx = self.create_stream_ip4(self.pg0, "232.1.1.1")
944 self.pg0.add_stream(tx)
946 self.pg_enable_capture(self.pg_interfaces)
949 rx = self.pg2.get_capture(257)
950 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [42])
951 rx = self.pg3.get_capture(257)
952 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [43])
954 def test_mcast_ip4_tail(self):
955 """ MPLS IPv4 Multicast Tail """
958 # Add a multicast route that will forward the traffic
961 route_232_1_1_1 = VppIpMRoute(
965 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
967 paths=[VppMRoutePath(self.pg1.sw_if_index,
968 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
969 route_232_1_1_1.add_vpp_config()
972 # An interface receive label that maps traffic to RX on interface
974 # by injecting the packet in on pg0, which is in table 0
975 # doing an rpf-id and matching a route in table 1
976 # if the packet egresses, then we must have matched the route in
979 route_34_eos = VppMplsRoute(self, 34, 1,
980 [VppRoutePath("0.0.0.0",
981 self.pg1.sw_if_index,
986 route_34_eos.add_vpp_config()
989 # Drop due to interface lookup miss
991 self.vapi.cli("clear trace")
992 tx = self.create_stream_labelled_ip4(self.pg0, [34],
993 dst_ip="232.1.1.1", n=1)
994 self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop none")
997 # set the RPF-ID of the enrtry to match the input packet's
999 route_232_1_1_1.update_rpf_id(55)
1001 self.vapi.cli("clear trace")
1002 tx = self.create_stream_labelled_ip4(self.pg0, [34],
1003 dst_ip="232.1.1.1", n=257)
1004 self.pg0.add_stream(tx)
1006 self.pg_enable_capture(self.pg_interfaces)
1009 rx = self.pg1.get_capture(257)
1010 self.verify_capture_ip4(self.pg1, rx, tx)
1013 # set the RPF-ID of the enrtry to not match the input packet's
1015 route_232_1_1_1.update_rpf_id(56)
1016 tx = self.create_stream_labelled_ip4(self.pg0, [34],
1018 self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
1020 def test_mcast_ip6_tail(self):
1021 """ MPLS IPv6 Multicast Tail """
1024 # Add a multicast route that will forward the traffic
1027 route_ff = VppIpMRoute(
1031 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
1033 paths=[VppMRoutePath(self.pg1.sw_if_index,
1034 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
1036 route_ff.add_vpp_config()
1039 # An interface receive label that maps traffic to RX on interface
1041 # by injecting the packet in on pg0, which is in table 0
1042 # doing an rpf-id and matching a route in table 1
1043 # if the packet egresses, then we must have matched the route in
1046 route_34_eos = VppMplsRoute(
1049 self.pg1.sw_if_index,
1055 route_34_eos.add_vpp_config()
1058 # Drop due to interface lookup miss
1060 tx = self.create_stream_labelled_ip6(self.pg0, [34], 255,
1064 # set the RPF-ID of the enrtry to match the input packet's
1066 route_ff.update_rpf_id(55)
1068 tx = self.create_stream_labelled_ip6(self.pg0, [34], 255,
1070 self.pg0.add_stream(tx)
1072 self.pg_enable_capture(self.pg_interfaces)
1075 rx = self.pg1.get_capture(257)
1076 self.verify_capture_ip6(self.pg1, rx, tx)
1079 # set the RPF-ID of the enrtry to not match the input packet's
1081 route_ff.update_rpf_id(56)
1082 tx = self.create_stream_labelled_ip6(self.pg0, [34], 225,
1084 self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
1087 class TestMPLSDisabled(VppTestCase):
1088 """ MPLS disabled """
1091 super(TestMPLSDisabled, self).setUp()
1093 # create 2 pg interfaces
1094 self.create_pg_interfaces(range(2))
1096 # PG0 is MPLS enalbed
1098 self.pg0.config_ip4()
1099 self.pg0.resolve_arp()
1100 self.pg0.enable_mpls()
1102 # PG 1 is not MPLS enabled
1106 super(TestMPLSDisabled, self).tearDown()
1107 for i in self.pg_interfaces:
1111 def send_and_assert_no_replies(self, intf, pkts, remark):
1112 intf.add_stream(pkts)
1113 self.pg_enable_capture(self.pg_interfaces)
1115 for i in self.pg_interfaces:
1117 i.assert_nothing_captured(remark=remark)
1119 def test_mpls_disabled(self):
1120 """ MPLS Disabled """
1122 tx = (Ether(src=self.pg1.remote_mac,
1123 dst=self.pg1.local_mac) /
1124 MPLS(label=32, ttl=64) /
1125 IPv6(src="2001::1", dst=self.pg0.remote_ip6) /
1126 UDP(sport=1234, dport=1234) /
1130 # A simple MPLS xconnect - eos label in label out
1132 route_32_eos = VppMplsRoute(self, 32, 1,
1133 [VppRoutePath(self.pg0.remote_ip4,
1134 self.pg0.sw_if_index,
1136 route_32_eos.add_vpp_config()
1139 # PG1 does not forward IP traffic
1141 self.send_and_assert_no_replies(self.pg1, tx, "MPLS disabled")
1146 self.pg1.enable_mpls()
1149 # Now we get packets through
1151 self.pg1.add_stream(tx)
1152 self.pg_enable_capture(self.pg_interfaces)
1155 rx = self.pg0.get_capture(1)
1160 self.pg1.disable_mpls()
1163 # PG1 does not forward IP traffic
1165 self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1166 self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1169 class TestMPLSPIC(VppTestCase):
1170 """ MPLS PIC edge convergence """
1173 super(TestMPLSPIC, self).setUp()
1175 # create 2 pg interfaces
1176 self.create_pg_interfaces(range(4))
1180 self.pg0.config_ip4()
1181 self.pg0.resolve_arp()
1182 self.pg0.enable_mpls()
1184 self.pg1.config_ip4()
1185 self.pg1.resolve_arp()
1186 self.pg1.enable_mpls()
1188 # VRF (customer facing) link
1190 self.pg2.set_table_ip4(1)
1191 self.pg2.config_ip4()
1192 self.pg2.resolve_arp()
1193 self.pg2.set_table_ip6(1)
1194 self.pg2.config_ip6()
1195 self.pg2.resolve_ndp()
1197 self.pg3.set_table_ip4(1)
1198 self.pg3.config_ip4()
1199 self.pg3.resolve_arp()
1200 self.pg3.set_table_ip6(1)
1201 self.pg3.config_ip6()
1202 self.pg3.resolve_ndp()
1205 super(TestMPLSPIC, self).tearDown()
1206 self.pg0.disable_mpls()
1207 for i in self.pg_interfaces:
1214 def test_mpls_ibgp_pic(self):
1215 """ MPLS iBGP PIC edge convergence
1217 1) setup many iBGP VPN routes via a pair of iBGP peers.
1218 2) Check EMCP forwarding to these peers
1219 3) withdraw the IGP route to one of these peers.
1220 4) check forwarding continues to the remaining peer
1224 # IGP+LDP core routes
1226 core_10_0_0_45 = VppIpRoute(self, "10.0.0.45", 32,
1227 [VppRoutePath(self.pg0.remote_ip4,
1228 self.pg0.sw_if_index,
1230 core_10_0_0_45.add_vpp_config()
1232 core_10_0_0_46 = VppIpRoute(self, "10.0.0.46", 32,
1233 [VppRoutePath(self.pg1.remote_ip4,
1234 self.pg1.sw_if_index,
1236 core_10_0_0_46.add_vpp_config()
1239 # Lot's of VPN routes. We need more the 64 so VPP will build
1240 # the fast convergence indirection
1244 for ii in range(64):
1245 dst = "192.168.1.%d" % ii
1246 vpn_routes.append(VppIpRoute(self, dst, 32,
1247 [VppRoutePath("10.0.0.45",
1251 VppRoutePath("10.0.0.46",
1254 is_resolve_host=1)],
1256 vpn_routes[ii].add_vpp_config()
1258 pkts.append(Ether(dst=self.pg2.local_mac,
1259 src=self.pg2.remote_mac) /
1260 IP(src=self.pg2.remote_ip4, dst=dst) /
1261 UDP(sport=1234, dport=1234) /
1265 # Send the packet stream (one pkt to each VPN route)
1266 # - expect a 50-50 split of the traffic
1268 self.pg2.add_stream(pkts)
1269 self.pg_enable_capture(self.pg_interfaces)
1272 rx0 = self.pg0._get_capture(1)
1273 rx1 = self.pg1._get_capture(1)
1275 # not testig the LB hashing algorithm so we're not concerned
1276 # with the split ratio, just as long as neither is 0
1277 self.assertNotEqual(0, len(rx0))
1278 self.assertNotEqual(0, len(rx1))
1281 # use a test CLI command to stop the FIB walk process, this
1282 # will prevent the FIB converging the VPN routes and thus allow
1283 # us to probe the interim (psot-fail, pre-converge) state
1285 self.vapi.ppcli("test fib-walk-process disable")
1288 # Withdraw one of the IGP routes
1290 core_10_0_0_46.remove_vpp_config()
1293 # now all packets should be forwarded through the remaining peer
1295 self.vapi.ppcli("clear trace")
1296 self.pg2.add_stream(pkts)
1297 self.pg_enable_capture(self.pg_interfaces)
1300 rx0 = self.pg0.get_capture(len(pkts))
1303 # enable the FIB walk process to converge the FIB
1305 self.vapi.ppcli("test fib-walk-process enable")
1308 # packets should still be forwarded through the remaining peer
1310 self.pg2.add_stream(pkts)
1311 self.pg_enable_capture(self.pg_interfaces)
1314 rx0 = self.pg0.get_capture(64)
1317 # Add the IGP route back and we return to load-balancing
1319 core_10_0_0_46.add_vpp_config()
1321 self.pg2.add_stream(pkts)
1322 self.pg_enable_capture(self.pg_interfaces)
1325 rx0 = self.pg0._get_capture(1)
1326 rx1 = self.pg1._get_capture(1)
1327 self.assertNotEqual(0, len(rx0))
1328 self.assertNotEqual(0, len(rx1))
1330 def test_mpls_ebgp_pic(self):
1331 """ MPLS eBGP PIC edge convergence
1333 1) setup many eBGP VPN routes via a pair of eBGP peers
1334 2) Check EMCP forwarding to these peers
1335 3) withdraw one eBGP path - expect LB across remaining eBGP
1339 # Lot's of VPN routes. We need more the 64 so VPP will build
1340 # the fast convergence indirection
1345 for ii in range(64):
1346 dst = "192.168.1.%d" % ii
1347 local_label = 1600 + ii
1348 vpn_routes.append(VppIpRoute(self, dst, 32,
1349 [VppRoutePath(self.pg2.remote_ip4,
1352 is_resolve_attached=1),
1353 VppRoutePath(self.pg3.remote_ip4,
1356 is_resolve_attached=1)],
1358 vpn_routes[ii].add_vpp_config()
1360 vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 32,
1362 vpn_bindings[ii].add_vpp_config()
1364 pkts.append(Ether(dst=self.pg0.local_mac,
1365 src=self.pg0.remote_mac) /
1366 MPLS(label=local_label, ttl=64) /
1367 IP(src=self.pg0.remote_ip4, dst=dst) /
1368 UDP(sport=1234, dport=1234) /
1371 self.pg0.add_stream(pkts)
1372 self.pg_enable_capture(self.pg_interfaces)
1375 rx0 = self.pg2._get_capture(1)
1376 rx1 = self.pg3._get_capture(1)
1377 self.assertNotEqual(0, len(rx0))
1378 self.assertNotEqual(0, len(rx1))
1381 # use a test CLI command to stop the FIB walk process, this
1382 # will prevent the FIB converging the VPN routes and thus allow
1383 # us to probe the interim (psot-fail, pre-converge) state
1385 self.vapi.ppcli("test fib-walk-process disable")
1388 # withdraw the connected prefix on the interface.
1390 self.pg2.unconfig_ip4()
1393 # now all packets should be forwarded through the remaining peer
1395 self.pg0.add_stream(pkts)
1396 self.pg_enable_capture(self.pg_interfaces)
1399 rx0 = self.pg3.get_capture(len(pkts))
1402 # enable the FIB walk process to converge the FIB
1404 self.vapi.ppcli("test fib-walk-process enable")
1405 self.pg0.add_stream(pkts)
1406 self.pg_enable_capture(self.pg_interfaces)
1409 rx0 = self.pg3.get_capture(len(pkts))
1412 # put the connecteds back
1414 self.pg2.config_ip4()
1416 self.pg0.add_stream(pkts)
1417 self.pg_enable_capture(self.pg_interfaces)
1420 rx0 = self.pg2._get_capture(1)
1421 rx1 = self.pg3._get_capture(1)
1422 self.assertNotEqual(0, len(rx0))
1423 self.assertNotEqual(0, len(rx1))
1425 def test_mpls_v6_ebgp_pic(self):
1426 """ MPLSv6 eBGP PIC edge convergence
1428 1) setup many eBGP VPNv6 routes via a pair of eBGP peers
1429 2) Check EMCP forwarding to these peers
1430 3) withdraw one eBGP path - expect LB across remaining eBGP
1434 # Lot's of VPN routes. We need more the 64 so VPP will build
1435 # the fast convergence indirection
1440 for ii in range(64):
1441 dst = "3000::%d" % ii
1442 local_label = 1600 + ii
1443 vpn_routes.append(VppIpRoute(self, dst, 128,
1444 [VppRoutePath(self.pg2.remote_ip6,
1447 is_resolve_attached=1,
1449 VppRoutePath(self.pg3.remote_ip6,
1453 is_resolve_attached=1)],
1456 vpn_routes[ii].add_vpp_config()
1458 vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 128,
1461 vpn_bindings[ii].add_vpp_config()
1463 pkts.append(Ether(dst=self.pg0.local_mac,
1464 src=self.pg0.remote_mac) /
1465 MPLS(label=local_label, ttl=64) /
1466 IPv6(src=self.pg0.remote_ip6, dst=dst) /
1467 UDP(sport=1234, dport=1234) /
1470 self.pg0.add_stream(pkts)
1471 self.pg_enable_capture(self.pg_interfaces)
1474 rx0 = self.pg2._get_capture(1)
1475 rx1 = self.pg3._get_capture(1)
1476 self.assertNotEqual(0, len(rx0))
1477 self.assertNotEqual(0, len(rx1))
1480 # use a test CLI command to stop the FIB walk process, this
1481 # will prevent the FIB converging the VPN routes and thus allow
1482 # us to probe the interim (psot-fail, pre-converge) state
1484 self.vapi.ppcli("test fib-walk-process disable")
1487 # withdraw the connected prefix on the interface.
1488 # and shutdown the interface so the ND cache is flushed.
1490 self.pg2.unconfig_ip6()
1491 self.pg2.admin_down()
1494 # now all packets should be forwarded through the remaining peer
1496 self.pg0.add_stream(pkts)
1497 self.pg_enable_capture(self.pg_interfaces)
1500 rx0 = self.pg3.get_capture(len(pkts))
1503 # enable the FIB walk process to converge the FIB
1505 self.vapi.ppcli("test fib-walk-process enable")
1506 self.pg0.add_stream(pkts)
1507 self.pg_enable_capture(self.pg_interfaces)
1510 rx0 = self.pg3.get_capture(len(pkts))
1513 # put the connecteds back
1516 self.pg2.config_ip6()
1518 self.pg0.add_stream(pkts)
1519 self.pg_enable_capture(self.pg_interfaces)
1522 rx0 = self.pg2._get_capture(1)
1523 rx1 = self.pg3._get_capture(1)
1524 self.assertNotEqual(0, len(rx0))
1525 self.assertNotEqual(0, len(rx1))
1528 if __name__ == '__main__':
1529 unittest.main(testRunner=VppTestRunner)