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)
774 route_35_eos.remove_vpp_config()
775 route_34_eos.remove_vpp_config()
777 def test_interface_rx(self):
778 """ MPLS Interface Receive """
781 # Add a non-recursive route that will forward the traffic
784 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
786 paths=[VppRoutePath(self.pg1.remote_ip4,
787 self.pg1.sw_if_index)])
788 route_10_0_0_1.add_vpp_config()
791 # An interface receive label that maps traffic to RX on interface
793 # by injecting the packet in on pg0, which is in table 0
794 # doing an interface-rx on pg1 and matching a route in table 1
795 # if the packet egresses, then we must have swapped to pg1
796 # so as to have matched the route in table 1
798 route_34_eos = VppMplsRoute(self, 34, 1,
799 [VppRoutePath("0.0.0.0",
800 self.pg1.sw_if_index,
802 route_34_eos.add_vpp_config()
805 # ping an interface in the default table
806 # PG0 is in the default table
808 self.vapi.cli("clear trace")
809 tx = self.create_stream_labelled_ip4(self.pg0, [34], n=257,
811 self.pg0.add_stream(tx)
813 self.pg_enable_capture(self.pg_interfaces)
816 rx = self.pg1.get_capture(257)
817 self.verify_capture_ip4(self.pg1, rx, tx)
819 def test_mcast_mid_point(self):
820 """ MPLS Multicast Mid Point """
823 # Add a non-recursive route that will forward the traffic
826 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
828 paths=[VppRoutePath(self.pg1.remote_ip4,
829 self.pg1.sw_if_index)])
830 route_10_0_0_1.add_vpp_config()
833 # Add a mcast entry that replicate to pg2 and pg3
834 # and replicate to a interface-rx (like a bud node would)
836 route_3400_eos = VppMplsRoute(self, 3400, 1,
837 [VppRoutePath(self.pg2.remote_ip4,
838 self.pg2.sw_if_index,
840 VppRoutePath(self.pg3.remote_ip4,
841 self.pg3.sw_if_index,
843 VppRoutePath("0.0.0.0",
844 self.pg1.sw_if_index,
847 route_3400_eos.add_vpp_config()
850 # ping an interface in the default table
851 # PG0 is in the default table
853 self.vapi.cli("clear trace")
854 tx = self.create_stream_labelled_ip4(self.pg0, [3400], n=257,
856 self.pg0.add_stream(tx)
858 self.pg_enable_capture(self.pg_interfaces)
861 rx = self.pg1.get_capture(257)
862 self.verify_capture_ip4(self.pg1, rx, tx)
864 rx = self.pg2.get_capture(257)
865 self.verify_capture_labelled(self.pg2, rx, tx, [3401])
866 rx = self.pg3.get_capture(257)
867 self.verify_capture_labelled(self.pg3, rx, tx, [3402])
869 def test_mcast_head(self):
870 """ MPLS Multicast Head-end """
873 # Create a multicast tunnel with two replications
875 mpls_tun = VppMPLSTunnelInterface(self,
876 [VppRoutePath(self.pg2.remote_ip4,
877 self.pg2.sw_if_index,
879 VppRoutePath(self.pg3.remote_ip4,
880 self.pg3.sw_if_index,
883 mpls_tun.add_vpp_config()
887 # add an unlabelled route through the new tunnel
889 route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
890 [VppRoutePath("0.0.0.0",
891 mpls_tun._sw_if_index)])
892 route_10_0_0_3.add_vpp_config()
894 self.vapi.cli("clear trace")
895 tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
896 self.pg0.add_stream(tx)
898 self.pg_enable_capture(self.pg_interfaces)
901 rx = self.pg2.get_capture(257)
902 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [42])
903 rx = self.pg3.get_capture(257)
904 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [43])
907 # An an IP multicast route via the tunnel
909 # one accepting interface, pg0, 1 forwarding interface via the tunnel
911 route_232_1_1_1 = VppIpMRoute(
915 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
916 [VppMRoutePath(self.pg0.sw_if_index,
917 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
918 VppMRoutePath(mpls_tun._sw_if_index,
919 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
920 route_232_1_1_1.add_vpp_config()
922 self.vapi.cli("clear trace")
923 tx = self.create_stream_ip4(self.pg0, "232.1.1.1")
924 self.pg0.add_stream(tx)
926 self.pg_enable_capture(self.pg_interfaces)
929 rx = self.pg2.get_capture(257)
930 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [42])
931 rx = self.pg3.get_capture(257)
932 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [43])
934 def test_mcast_ip4_tail(self):
935 """ MPLS IPv4 Multicast Tail """
938 # Add a multicast route that will forward the traffic
941 route_232_1_1_1 = VppIpMRoute(
945 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
947 paths=[VppMRoutePath(self.pg1.sw_if_index,
948 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
949 route_232_1_1_1.add_vpp_config()
952 # An interface receive label that maps traffic to RX on interface
954 # by injecting the packet in on pg0, which is in table 0
955 # doing an rpf-id and matching a route in table 1
956 # if the packet egresses, then we must have matched the route in
959 route_34_eos = VppMplsRoute(self, 34, 1,
960 [VppRoutePath("0.0.0.0",
961 self.pg1.sw_if_index,
966 route_34_eos.add_vpp_config()
969 # Drop due to interface lookup miss
971 self.vapi.cli("clear trace")
972 tx = self.create_stream_labelled_ip4(self.pg0, [34],
973 dst_ip="232.1.1.1", n=1)
974 self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop none")
977 # set the RPF-ID of the enrtry to match the input packet's
979 route_232_1_1_1.update_rpf_id(55)
981 self.vapi.cli("clear trace")
982 tx = self.create_stream_labelled_ip4(self.pg0, [34],
983 dst_ip="232.1.1.1", n=257)
984 self.pg0.add_stream(tx)
986 self.pg_enable_capture(self.pg_interfaces)
989 rx = self.pg1.get_capture(257)
990 self.verify_capture_ip4(self.pg1, rx, tx)
993 # set the RPF-ID of the enrtry to not match the input packet's
995 route_232_1_1_1.update_rpf_id(56)
996 tx = self.create_stream_labelled_ip4(self.pg0, [34],
998 self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
1000 def test_mcast_ip6_tail(self):
1001 """ MPLS IPv6 Multicast Tail """
1004 # Add a multicast route that will forward the traffic
1007 route_ff = VppIpMRoute(
1011 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
1013 paths=[VppMRoutePath(self.pg1.sw_if_index,
1014 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
1016 route_ff.add_vpp_config()
1019 # An interface receive label that maps traffic to RX on interface
1021 # by injecting the packet in on pg0, which is in table 0
1022 # doing an rpf-id and matching a route in table 1
1023 # if the packet egresses, then we must have matched the route in
1026 route_34_eos = VppMplsRoute(
1029 self.pg1.sw_if_index,
1035 route_34_eos.add_vpp_config()
1038 # Drop due to interface lookup miss
1040 tx = self.create_stream_labelled_ip6(self.pg0, [34], 255,
1044 # set the RPF-ID of the enrtry to match the input packet's
1046 route_ff.update_rpf_id(55)
1048 tx = self.create_stream_labelled_ip6(self.pg0, [34], 255,
1050 self.pg0.add_stream(tx)
1052 self.pg_enable_capture(self.pg_interfaces)
1055 rx = self.pg1.get_capture(257)
1056 self.verify_capture_ip6(self.pg1, rx, tx)
1059 # set the RPF-ID of the enrtry to not match the input packet's
1061 route_ff.update_rpf_id(56)
1062 tx = self.create_stream_labelled_ip6(self.pg0, [34], 225,
1064 self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
1067 class TestMPLSDisabled(VppTestCase):
1068 """ MPLS disabled """
1071 super(TestMPLSDisabled, self).setUp()
1073 # create 2 pg interfaces
1074 self.create_pg_interfaces(range(2))
1076 # PG0 is MPLS enalbed
1078 self.pg0.config_ip4()
1079 self.pg0.resolve_arp()
1080 self.pg0.enable_mpls()
1082 # PG 1 is not MPLS enabled
1086 super(TestMPLSDisabled, self).tearDown()
1087 for i in self.pg_interfaces:
1091 def send_and_assert_no_replies(self, intf, pkts, remark):
1092 intf.add_stream(pkts)
1093 self.pg_enable_capture(self.pg_interfaces)
1095 for i in self.pg_interfaces:
1097 i.assert_nothing_captured(remark=remark)
1099 def test_mpls_disabled(self):
1100 """ MPLS Disabled """
1102 tx = (Ether(src=self.pg1.remote_mac,
1103 dst=self.pg1.local_mac) /
1104 MPLS(label=32, ttl=64) /
1105 IPv6(src="2001::1", dst=self.pg0.remote_ip6) /
1106 UDP(sport=1234, dport=1234) /
1110 # A simple MPLS xconnect - eos label in label out
1112 route_32_eos = VppMplsRoute(self, 32, 1,
1113 [VppRoutePath(self.pg0.remote_ip4,
1114 self.pg0.sw_if_index,
1116 route_32_eos.add_vpp_config()
1119 # PG1 does not forward IP traffic
1121 self.send_and_assert_no_replies(self.pg1, tx, "MPLS disabled")
1126 self.pg1.enable_mpls()
1129 # Now we get packets through
1131 self.pg1.add_stream(tx)
1132 self.pg_enable_capture(self.pg_interfaces)
1135 rx = self.pg0.get_capture(1)
1140 self.pg1.disable_mpls()
1143 # PG1 does not forward IP traffic
1145 self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1146 self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1149 class TestMPLSPIC(VppTestCase):
1150 """ MPLS PIC edge convergence """
1153 super(TestMPLSPIC, self).setUp()
1155 # create 2 pg interfaces
1156 self.create_pg_interfaces(range(4))
1160 self.pg0.config_ip4()
1161 self.pg0.resolve_arp()
1162 self.pg0.enable_mpls()
1164 self.pg1.config_ip4()
1165 self.pg1.resolve_arp()
1166 self.pg1.enable_mpls()
1168 # VRF (customer facing) link
1170 self.pg2.set_table_ip4(1)
1171 self.pg2.config_ip4()
1172 self.pg2.resolve_arp()
1173 self.pg2.set_table_ip6(1)
1174 self.pg2.config_ip6()
1175 self.pg2.resolve_ndp()
1177 self.pg3.set_table_ip4(1)
1178 self.pg3.config_ip4()
1179 self.pg3.resolve_arp()
1180 self.pg3.set_table_ip6(1)
1181 self.pg3.config_ip6()
1182 self.pg3.resolve_ndp()
1185 super(TestMPLSPIC, self).tearDown()
1186 self.pg0.disable_mpls()
1187 for i in self.pg_interfaces:
1194 def test_mpls_ibgp_pic(self):
1195 """ MPLS iBGP PIC edge convergence
1197 1) setup many iBGP VPN routes via a pair of iBGP peers.
1198 2) Check EMCP forwarding to these peers
1199 3) withdraw the IGP route to one of these peers.
1200 4) check forwarding continues to the remaining peer
1204 # IGP+LDP core routes
1206 core_10_0_0_45 = VppIpRoute(self, "10.0.0.45", 32,
1207 [VppRoutePath(self.pg0.remote_ip4,
1208 self.pg0.sw_if_index,
1210 core_10_0_0_45.add_vpp_config()
1212 core_10_0_0_46 = VppIpRoute(self, "10.0.0.46", 32,
1213 [VppRoutePath(self.pg1.remote_ip4,
1214 self.pg1.sw_if_index,
1216 core_10_0_0_46.add_vpp_config()
1219 # Lot's of VPN routes. We need more the 64 so VPP will build
1220 # the fast convergence indirection
1224 for ii in range(64):
1225 dst = "192.168.1.%d" % ii
1226 vpn_routes.append(VppIpRoute(self, dst, 32,
1227 [VppRoutePath("10.0.0.45",
1231 VppRoutePath("10.0.0.46",
1234 is_resolve_host=1)],
1236 vpn_routes[ii].add_vpp_config()
1238 pkts.append(Ether(dst=self.pg2.local_mac,
1239 src=self.pg2.remote_mac) /
1240 IP(src=self.pg2.remote_ip4, dst=dst) /
1241 UDP(sport=1234, dport=1234) /
1245 # Send the packet stream (one pkt to each VPN route)
1246 # - expect a 50-50 split of the traffic
1248 self.pg2.add_stream(pkts)
1249 self.pg_enable_capture(self.pg_interfaces)
1252 rx0 = self.pg0._get_capture(1)
1253 rx1 = self.pg1._get_capture(1)
1255 # not testig the LB hashing algorithm so we're not concerned
1256 # with the split ratio, just as long as neither is 0
1257 self.assertNotEqual(0, len(rx0))
1258 self.assertNotEqual(0, len(rx1))
1261 # use a test CLI command to stop the FIB walk process, this
1262 # will prevent the FIB converging the VPN routes and thus allow
1263 # us to probe the interim (psot-fail, pre-converge) state
1265 self.vapi.ppcli("test fib-walk-process disable")
1268 # Withdraw one of the IGP routes
1270 core_10_0_0_46.remove_vpp_config()
1273 # now all packets should be forwarded through the remaining peer
1275 self.vapi.ppcli("clear trace")
1276 self.pg2.add_stream(pkts)
1277 self.pg_enable_capture(self.pg_interfaces)
1280 rx0 = self.pg0.get_capture(len(pkts))
1283 # enable the FIB walk process to converge the FIB
1285 self.vapi.ppcli("test fib-walk-process enable")
1288 # packets should still be forwarded through the remaining peer
1290 self.pg2.add_stream(pkts)
1291 self.pg_enable_capture(self.pg_interfaces)
1294 rx0 = self.pg0.get_capture(64)
1297 # Add the IGP route back and we return to load-balancing
1299 core_10_0_0_46.add_vpp_config()
1301 self.pg2.add_stream(pkts)
1302 self.pg_enable_capture(self.pg_interfaces)
1305 rx0 = self.pg0._get_capture(1)
1306 rx1 = self.pg1._get_capture(1)
1307 self.assertNotEqual(0, len(rx0))
1308 self.assertNotEqual(0, len(rx1))
1310 def test_mpls_ebgp_pic(self):
1311 """ MPLS eBGP PIC edge convergence
1313 1) setup many eBGP VPN routes via a pair of eBGP peers
1314 2) Check EMCP forwarding to these peers
1315 3) withdraw one eBGP path - expect LB across remaining eBGP
1319 # Lot's of VPN routes. We need more the 64 so VPP will build
1320 # the fast convergence indirection
1325 for ii in range(64):
1326 dst = "192.168.1.%d" % ii
1327 local_label = 1600 + ii
1328 vpn_routes.append(VppIpRoute(self, dst, 32,
1329 [VppRoutePath(self.pg2.remote_ip4,
1332 is_resolve_attached=1),
1333 VppRoutePath(self.pg3.remote_ip4,
1336 is_resolve_attached=1)],
1338 vpn_routes[ii].add_vpp_config()
1340 vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 32,
1342 vpn_bindings[ii].add_vpp_config()
1344 pkts.append(Ether(dst=self.pg0.local_mac,
1345 src=self.pg0.remote_mac) /
1346 MPLS(label=local_label, ttl=64) /
1347 IP(src=self.pg0.remote_ip4, dst=dst) /
1348 UDP(sport=1234, dport=1234) /
1351 self.pg0.add_stream(pkts)
1352 self.pg_enable_capture(self.pg_interfaces)
1355 rx0 = self.pg2._get_capture(1)
1356 rx1 = self.pg3._get_capture(1)
1357 self.assertNotEqual(0, len(rx0))
1358 self.assertNotEqual(0, len(rx1))
1361 # use a test CLI command to stop the FIB walk process, this
1362 # will prevent the FIB converging the VPN routes and thus allow
1363 # us to probe the interim (psot-fail, pre-converge) state
1365 self.vapi.ppcli("test fib-walk-process disable")
1368 # withdraw the connected prefix on the interface.
1370 self.pg2.unconfig_ip4()
1373 # now all packets should be forwarded through the remaining peer
1375 self.pg0.add_stream(pkts)
1376 self.pg_enable_capture(self.pg_interfaces)
1379 rx0 = self.pg3.get_capture(len(pkts))
1382 # enable the FIB walk process to converge the FIB
1384 self.vapi.ppcli("test fib-walk-process enable")
1385 self.pg0.add_stream(pkts)
1386 self.pg_enable_capture(self.pg_interfaces)
1389 rx0 = self.pg3.get_capture(len(pkts))
1392 # put the connecteds back
1394 self.pg2.config_ip4()
1396 self.pg0.add_stream(pkts)
1397 self.pg_enable_capture(self.pg_interfaces)
1400 rx0 = self.pg2._get_capture(1)
1401 rx1 = self.pg3._get_capture(1)
1402 self.assertNotEqual(0, len(rx0))
1403 self.assertNotEqual(0, len(rx1))
1405 def test_mpls_v6_ebgp_pic(self):
1406 """ MPLSv6 eBGP PIC edge convergence
1408 1) setup many eBGP VPNv6 routes via a pair of eBGP peers
1409 2) Check EMCP forwarding to these peers
1410 3) withdraw one eBGP path - expect LB across remaining eBGP
1414 # Lot's of VPN routes. We need more the 64 so VPP will build
1415 # the fast convergence indirection
1420 for ii in range(64):
1421 dst = "3000::%d" % ii
1422 local_label = 1600 + ii
1423 vpn_routes.append(VppIpRoute(self, dst, 128,
1424 [VppRoutePath(self.pg2.remote_ip6,
1427 is_resolve_attached=1,
1429 VppRoutePath(self.pg3.remote_ip6,
1433 is_resolve_attached=1)],
1436 vpn_routes[ii].add_vpp_config()
1438 vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 128,
1441 vpn_bindings[ii].add_vpp_config()
1443 pkts.append(Ether(dst=self.pg0.local_mac,
1444 src=self.pg0.remote_mac) /
1445 MPLS(label=local_label, ttl=64) /
1446 IPv6(src=self.pg0.remote_ip6, dst=dst) /
1447 UDP(sport=1234, dport=1234) /
1450 self.pg0.add_stream(pkts)
1451 self.pg_enable_capture(self.pg_interfaces)
1454 rx0 = self.pg2._get_capture(1)
1455 rx1 = self.pg3._get_capture(1)
1456 self.assertNotEqual(0, len(rx0))
1457 self.assertNotEqual(0, len(rx1))
1460 # use a test CLI command to stop the FIB walk process, this
1461 # will prevent the FIB converging the VPN routes and thus allow
1462 # us to probe the interim (psot-fail, pre-converge) state
1464 self.vapi.ppcli("test fib-walk-process disable")
1467 # withdraw the connected prefix on the interface.
1468 # and shutdown the interface so the ND cache is flushed.
1470 self.pg2.unconfig_ip6()
1471 self.pg2.admin_down()
1474 # now all packets should be forwarded through the remaining peer
1476 self.pg0.add_stream(pkts)
1477 self.pg_enable_capture(self.pg_interfaces)
1480 rx0 = self.pg3.get_capture(len(pkts))
1483 # enable the FIB walk process to converge the FIB
1485 self.vapi.ppcli("test fib-walk-process enable")
1486 self.pg0.add_stream(pkts)
1487 self.pg_enable_capture(self.pg_interfaces)
1490 rx0 = self.pg3.get_capture(len(pkts))
1493 # put the connecteds back
1496 self.pg2.config_ip6()
1498 self.pg0.add_stream(pkts)
1499 self.pg_enable_capture(self.pg_interfaces)
1502 rx0 = self.pg2._get_capture(1)
1503 rx1 = self.pg3._get_capture(1)
1504 self.assertNotEqual(0, len(rx0))
1505 self.assertNotEqual(0, len(rx1))
1508 if __name__ == '__main__':
1509 unittest.main(testRunner=VppTestRunner)