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):
106 self.reset_packet_infos()
108 for i in range(0, 257):
109 info = self.create_packet_info(src_if, src_if)
110 payload = self.info_to_payload(info)
111 p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
112 MPLS(label=mpls_label, ttl=mpls_ttl) /
113 IPv6(src=src_if.remote_ip6, dst=src_if.remote_ip6) /
114 UDP(sport=1234, dport=1234) /
121 def verify_filter(capture, sent):
122 if not len(capture) == len(sent):
123 # filter out any IPv6 RAs from the capture
129 def verify_capture_ip4(self, src_if, capture, sent, ping_resp=0):
131 capture = self.verify_filter(capture, sent)
133 self.assertEqual(len(capture), len(sent))
135 for i in range(len(capture)):
139 # the rx'd packet has the MPLS label popped
141 self.assertEqual(eth.type, 0x800)
147 self.assertEqual(rx_ip.src, tx_ip.src)
148 self.assertEqual(rx_ip.dst, tx_ip.dst)
149 # IP processing post pop has decremented the TTL
150 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
152 self.assertEqual(rx_ip.src, tx_ip.dst)
153 self.assertEqual(rx_ip.dst, tx_ip.src)
158 def verify_mpls_stack(self, rx, mpls_labels, ttl=255, num=0):
159 # the rx'd packet has the MPLS label popped
161 self.assertEqual(eth.type, 0x8847)
165 for ii in range(len(mpls_labels)):
166 self.assertEqual(rx_mpls.label, mpls_labels[ii])
167 self.assertEqual(rx_mpls.cos, 0)
169 self.assertEqual(rx_mpls.ttl, ttl)
171 self.assertEqual(rx_mpls.ttl, 255)
173 if ii == len(mpls_labels) - 1:
174 self.assertEqual(rx_mpls.s, 1)
177 self.assertEqual(rx_mpls.s, 0)
178 # pop the label to expose the next
179 rx_mpls = rx_mpls[MPLS].payload
181 def verify_capture_labelled_ip4(self, src_if, capture, sent,
184 capture = self.verify_filter(capture, sent)
186 self.assertEqual(len(capture), len(sent))
188 for i in range(len(capture)):
194 # the MPLS TTL is copied from the IP
195 self.verify_mpls_stack(
196 rx, mpls_labels, rx_ip.ttl, len(mpls_labels) - 1)
198 self.assertEqual(rx_ip.src, tx_ip.src)
199 self.assertEqual(rx_ip.dst, tx_ip.dst)
200 # IP processing post pop has decremented the TTL
201 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
206 def verify_capture_tunneled_ip4(self, src_if, capture, sent, mpls_labels,
209 top = len(mpls_labels) - 1
211 capture = self.verify_filter(capture, sent)
213 self.assertEqual(len(capture), len(sent))
215 for i in range(len(capture)):
221 # the MPLS TTL is 255 since it enters a new tunnel
222 self.verify_mpls_stack(
223 rx, mpls_labels, ttl, top)
225 self.assertEqual(rx_ip.src, tx_ip.src)
226 self.assertEqual(rx_ip.dst, tx_ip.dst)
227 # IP processing post pop has decremented the TTL
228 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
233 def verify_capture_labelled(self, src_if, capture, sent,
234 mpls_labels, ttl=254, num=0):
236 capture = self.verify_filter(capture, sent)
238 self.assertEqual(len(capture), len(sent))
240 for i in range(len(capture)):
242 self.verify_mpls_stack(rx, mpls_labels, ttl, num)
246 def verify_capture_ip6(self, src_if, capture, sent):
248 self.assertEqual(len(capture), len(sent))
250 for i in range(len(capture)):
254 # the rx'd packet has the MPLS label popped
256 self.assertEqual(eth.type, 0x86DD)
261 self.assertEqual(rx_ip.src, tx_ip.src)
262 self.assertEqual(rx_ip.dst, tx_ip.dst)
263 # IP processing post pop has decremented the TTL
264 self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)
269 def send_and_assert_no_replies(self, intf, pkts, remark):
270 intf.add_stream(pkts)
271 self.pg_enable_capture(self.pg_interfaces)
273 for i in self.pg_interfaces:
274 i.assert_nothing_captured(remark=remark)
277 """ MPLS label swap tests """
280 # A simple MPLS xconnect - eos label in label out
282 route_32_eos = VppMplsRoute(self, 32, 1,
283 [VppRoutePath(self.pg0.remote_ip4,
284 self.pg0.sw_if_index,
286 route_32_eos.add_vpp_config()
289 # a stream that matches the route for 10.0.0.1
290 # PG0 is in the default table
292 self.vapi.cli("clear trace")
293 tx = self.create_stream_labelled_ip4(self.pg0, [32])
294 self.pg0.add_stream(tx)
296 self.pg_enable_capture(self.pg_interfaces)
299 rx = self.pg0.get_capture()
300 self.verify_capture_labelled(self.pg0, rx, tx, [33])
303 # A simple MPLS xconnect - non-eos label in label out
305 route_32_neos = VppMplsRoute(self, 32, 0,
306 [VppRoutePath(self.pg0.remote_ip4,
307 self.pg0.sw_if_index,
309 route_32_neos.add_vpp_config()
312 # a stream that matches the route for 10.0.0.1
313 # PG0 is in the default table
315 self.vapi.cli("clear trace")
316 tx = self.create_stream_labelled_ip4(self.pg0, [32, 99])
317 self.pg0.add_stream(tx)
319 self.pg_enable_capture(self.pg_interfaces)
322 rx = self.pg0.get_capture()
323 self.verify_capture_labelled(self.pg0, rx, tx, [33, 99])
326 # An MPLS xconnect - EOS label in IP out
328 route_33_eos = VppMplsRoute(self, 33, 1,
329 [VppRoutePath(self.pg0.remote_ip4,
330 self.pg0.sw_if_index,
332 route_33_eos.add_vpp_config()
334 self.vapi.cli("clear trace")
335 tx = self.create_stream_labelled_ip4(self.pg0, [33])
336 self.pg0.add_stream(tx)
338 self.pg_enable_capture(self.pg_interfaces)
341 rx = self.pg0.get_capture()
342 self.verify_capture_ip4(self.pg0, rx, tx)
345 # An MPLS xconnect - non-EOS label in IP out - an invalid configuration
346 # so this traffic should be dropped.
348 route_33_neos = VppMplsRoute(self, 33, 0,
349 [VppRoutePath(self.pg0.remote_ip4,
350 self.pg0.sw_if_index,
352 route_33_neos.add_vpp_config()
354 self.vapi.cli("clear trace")
355 tx = self.create_stream_labelled_ip4(self.pg0, [33, 99])
356 self.pg0.add_stream(tx)
358 self.pg_enable_capture(self.pg_interfaces)
360 self.pg0.assert_nothing_captured(
361 remark="MPLS non-EOS packets popped and forwarded")
364 # A recursive EOS x-connect, which resolves through another x-connect
366 route_34_eos = VppMplsRoute(self, 34, 1,
367 [VppRoutePath("0.0.0.0",
371 route_34_eos.add_vpp_config()
373 tx = self.create_stream_labelled_ip4(self.pg0, [34])
374 self.pg0.add_stream(tx)
376 self.pg_enable_capture(self.pg_interfaces)
379 rx = self.pg0.get_capture()
380 self.verify_capture_labelled(self.pg0, rx, tx, [33, 44, 45], num=2)
383 # A recursive non-EOS x-connect, which resolves through another
386 route_34_neos = VppMplsRoute(self, 34, 0,
387 [VppRoutePath("0.0.0.0",
391 route_34_neos.add_vpp_config()
393 self.vapi.cli("clear trace")
394 tx = self.create_stream_labelled_ip4(self.pg0, [34, 99])
395 self.pg0.add_stream(tx)
397 self.pg_enable_capture(self.pg_interfaces)
400 rx = self.pg0.get_capture()
401 # it's the 2nd (counting from 0) label in the stack that is swapped
402 self.verify_capture_labelled(self.pg0, rx, tx, [33, 44, 46, 99], num=2)
405 # an recursive IP route that resolves through the recursive non-eos
408 ip_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
409 [VppRoutePath("0.0.0.0",
413 ip_10_0_0_1.add_vpp_config()
415 self.vapi.cli("clear trace")
416 tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
417 self.pg0.add_stream(tx)
419 self.pg_enable_capture(self.pg_interfaces)
422 rx = self.pg0.get_capture()
423 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [33, 44, 46, 55])
425 ip_10_0_0_1.remove_vpp_config()
426 route_34_neos.remove_vpp_config()
427 route_34_eos.remove_vpp_config()
428 route_33_neos.remove_vpp_config()
429 route_33_eos.remove_vpp_config()
430 route_32_neos.remove_vpp_config()
431 route_32_eos.remove_vpp_config()
434 """ MPLS Local Label Binding test """
437 # Add a non-recursive route with a single out label
439 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
440 [VppRoutePath(self.pg0.remote_ip4,
441 self.pg0.sw_if_index,
443 route_10_0_0_1.add_vpp_config()
445 # bind a local label to the route
446 binding = VppMplsIpBind(self, 44, "10.0.0.1", 32)
447 binding.add_vpp_config()
450 self.vapi.cli("clear trace")
451 tx = self.create_stream_labelled_ip4(self.pg0, [44, 99])
452 self.pg0.add_stream(tx)
454 self.pg_enable_capture(self.pg_interfaces)
457 rx = self.pg0.get_capture()
458 self.verify_capture_labelled(self.pg0, rx, tx, [45, 99])
461 self.vapi.cli("clear trace")
462 tx = self.create_stream_labelled_ip4(self.pg0, [44])
463 self.pg0.add_stream(tx)
465 self.pg_enable_capture(self.pg_interfaces)
468 rx = self.pg0.get_capture()
469 self.verify_capture_labelled(self.pg0, rx, tx, [45])
472 self.vapi.cli("clear trace")
473 tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
474 self.pg0.add_stream(tx)
476 self.pg_enable_capture(self.pg_interfaces)
479 rx = self.pg0.get_capture()
480 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [45])
485 binding.remove_vpp_config()
486 route_10_0_0_1.remove_vpp_config()
488 def test_imposition(self):
489 """ MPLS label imposition test """
492 # Add a non-recursive route with a single out label
494 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
495 [VppRoutePath(self.pg0.remote_ip4,
496 self.pg0.sw_if_index,
498 route_10_0_0_1.add_vpp_config()
501 # a stream that matches the route for 10.0.0.1
502 # PG0 is in the default table
504 self.vapi.cli("clear trace")
505 tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
506 self.pg0.add_stream(tx)
508 self.pg_enable_capture(self.pg_interfaces)
511 rx = self.pg0.get_capture()
512 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32])
515 # Add a non-recursive route with a 3 out labels
517 route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
518 [VppRoutePath(self.pg0.remote_ip4,
519 self.pg0.sw_if_index,
520 labels=[32, 33, 34])])
521 route_10_0_0_2.add_vpp_config()
524 # a stream that matches the route for 10.0.0.1
525 # PG0 is in the default table
527 self.vapi.cli("clear trace")
528 tx = self.create_stream_ip4(self.pg0, "10.0.0.2")
529 self.pg0.add_stream(tx)
531 self.pg_enable_capture(self.pg_interfaces)
534 rx = self.pg0.get_capture()
535 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32, 33, 34])
538 # add a recursive path, with output label, via the 1 label route
540 route_11_0_0_1 = VppIpRoute(self, "11.0.0.1", 32,
541 [VppRoutePath("10.0.0.1",
544 route_11_0_0_1.add_vpp_config()
547 # a stream that matches the route for 11.0.0.1, should pick up
548 # the label stack for 11.0.0.1 and 10.0.0.1
550 self.vapi.cli("clear trace")
551 tx = self.create_stream_ip4(self.pg0, "11.0.0.1")
552 self.pg0.add_stream(tx)
554 self.pg_enable_capture(self.pg_interfaces)
557 rx = self.pg0.get_capture()
558 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32, 44])
561 # add a recursive path, with 2 labels, via the 3 label route
563 route_11_0_0_2 = VppIpRoute(self, "11.0.0.2", 32,
564 [VppRoutePath("10.0.0.2",
567 route_11_0_0_2.add_vpp_config()
570 # a stream that matches the route for 11.0.0.1, should pick up
571 # the label stack for 11.0.0.1 and 10.0.0.1
573 self.vapi.cli("clear trace")
574 tx = self.create_stream_ip4(self.pg0, "11.0.0.2")
575 self.pg0.add_stream(tx)
577 self.pg_enable_capture(self.pg_interfaces)
580 rx = self.pg0.get_capture()
581 self.verify_capture_labelled_ip4(
582 self.pg0, rx, tx, [32, 33, 34, 44, 45])
587 route_11_0_0_2.remove_vpp_config()
588 route_11_0_0_1.remove_vpp_config()
589 route_10_0_0_2.remove_vpp_config()
590 route_10_0_0_1.remove_vpp_config()
592 def test_tunnel(self):
593 """ MPLS Tunnel Tests """
596 # Create a tunnel with a single out label
598 mpls_tun = VppMPLSTunnelInterface(self,
599 [VppRoutePath(self.pg0.remote_ip4,
600 self.pg0.sw_if_index,
602 mpls_tun.add_vpp_config()
606 # add an unlabelled route through the new tunnel
608 route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
609 [VppRoutePath("0.0.0.0",
610 mpls_tun._sw_if_index)])
611 route_10_0_0_3.add_vpp_config()
613 self.vapi.cli("clear trace")
614 tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
615 self.pg0.add_stream(tx)
617 self.pg_enable_capture(self.pg_interfaces)
620 rx = self.pg0.get_capture()
621 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [44, 46])
624 # add a labelled route through the new tunnel
626 route_10_0_0_4 = VppIpRoute(self, "10.0.0.4", 32,
627 [VppRoutePath("0.0.0.0",
628 mpls_tun._sw_if_index,
630 route_10_0_0_4.add_vpp_config()
632 self.vapi.cli("clear trace")
633 tx = self.create_stream_ip4(self.pg0, "10.0.0.4")
634 self.pg0.add_stream(tx)
636 self.pg_enable_capture(self.pg_interfaces)
639 rx = self.pg0.get_capture()
640 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [44, 46, 33],
643 def test_v4_exp_null(self):
644 """ MPLS V4 Explicit NULL test """
647 # The first test case has an MPLS TTL of 0
648 # all packet should be dropped
650 tx = self.create_stream_labelled_ip4(self.pg0, [0], 0)
651 self.pg0.add_stream(tx)
653 self.pg_enable_capture(self.pg_interfaces)
656 self.pg0.assert_nothing_captured(remark="MPLS TTL=0 packets forwarded")
659 # a stream with a non-zero MPLS TTL
660 # PG0 is in the default table
662 tx = self.create_stream_labelled_ip4(self.pg0, [0])
663 self.pg0.add_stream(tx)
665 self.pg_enable_capture(self.pg_interfaces)
668 rx = self.pg0.get_capture()
669 self.verify_capture_ip4(self.pg0, rx, tx)
672 # a stream with a non-zero MPLS TTL
674 # we are ensuring the post-pop lookup occurs in the VRF table
676 self.vapi.cli("clear trace")
677 tx = self.create_stream_labelled_ip4(self.pg1, [0])
678 self.pg1.add_stream(tx)
680 self.pg_enable_capture(self.pg_interfaces)
683 rx = self.pg1.get_capture()
684 self.verify_capture_ip4(self.pg0, rx, tx)
686 def test_v6_exp_null(self):
687 """ MPLS V6 Explicit NULL test """
690 # a stream with a non-zero MPLS TTL
691 # PG0 is in the default table
693 self.vapi.cli("clear trace")
694 tx = self.create_stream_labelled_ip6(self.pg0, 2, 2)
695 self.pg0.add_stream(tx)
697 self.pg_enable_capture(self.pg_interfaces)
700 rx = self.pg0.get_capture()
701 self.verify_capture_ip6(self.pg0, rx, tx)
704 # a stream with a non-zero MPLS TTL
706 # we are ensuring the post-pop lookup occurs in the VRF table
708 self.vapi.cli("clear trace")
709 tx = self.create_stream_labelled_ip6(self.pg1, 2, 2)
710 self.pg1.add_stream(tx)
712 self.pg_enable_capture(self.pg_interfaces)
715 rx = self.pg1.get_capture()
716 self.verify_capture_ip6(self.pg0, rx, tx)
722 # A de-agg route - next-hop lookup in default table
724 route_34_eos = VppMplsRoute(self, 34, 1,
725 [VppRoutePath("0.0.0.0",
728 route_34_eos.add_vpp_config()
731 # ping an interface in the default table
732 # PG0 is in the default table
734 self.vapi.cli("clear trace")
735 tx = self.create_stream_labelled_ip4(self.pg0, [34], ping=1,
737 self.pg0.add_stream(tx)
739 self.pg_enable_capture(self.pg_interfaces)
742 rx = self.pg0.get_capture()
743 self.verify_capture_ip4(self.pg0, rx, tx, ping_resp=1)
746 # A de-agg route - next-hop lookup in non-default table
748 route_35_eos = VppMplsRoute(self, 35, 1,
749 [VppRoutePath("0.0.0.0",
752 route_35_eos.add_vpp_config()
755 # ping an interface in the non-default table
756 # PG0 is in the default table. packet arrive labelled in the
757 # default table and egress unlabelled in the non-default
759 self.vapi.cli("clear trace")
760 tx = self.create_stream_labelled_ip4(
761 self.pg0, [35], ping=1, ip_itf=self.pg1)
762 self.pg0.add_stream(tx)
764 self.pg_enable_capture(self.pg_interfaces)
767 packet_count = self.get_packet_count_for_if_idx(self.pg0.sw_if_index)
768 rx = self.pg1.get_capture(packet_count)
769 self.verify_capture_ip4(self.pg1, rx, tx, ping_resp=1)
771 route_35_eos.remove_vpp_config()
772 route_34_eos.remove_vpp_config()
774 def test_interface_rx(self):
775 """ MPLS Interface Receive """
778 # Add a non-recursive route that will forward the traffic
781 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
783 paths=[VppRoutePath(self.pg1.remote_ip4,
784 self.pg1.sw_if_index)])
785 route_10_0_0_1.add_vpp_config()
788 # An interface receive label that maps traffic to RX on interface
790 # by injecting the packet in on pg0, which is in table 0
791 # doing an interface-rx on pg1 and matching a route in table 1
792 # if the packet egresses, then we must have swapped to pg1
793 # so as to have matched the route in table 1
795 route_34_eos = VppMplsRoute(self, 34, 1,
796 [VppRoutePath("0.0.0.0",
797 self.pg1.sw_if_index,
799 route_34_eos.add_vpp_config()
802 # ping an interface in the default table
803 # PG0 is in the default table
805 self.vapi.cli("clear trace")
806 tx = self.create_stream_labelled_ip4(self.pg0, [34], n=257,
808 self.pg0.add_stream(tx)
810 self.pg_enable_capture(self.pg_interfaces)
813 rx = self.pg1.get_capture(257)
814 self.verify_capture_ip4(self.pg1, rx, tx)
816 def test_mcast_mid_point(self):
817 """ MPLS Multicast Mid Point """
820 # Add a non-recursive route that will forward the traffic
823 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
825 paths=[VppRoutePath(self.pg1.remote_ip4,
826 self.pg1.sw_if_index)])
827 route_10_0_0_1.add_vpp_config()
830 # Add a mcast entry that replicate to pg2 and pg3
831 # and replicate to a interface-rx (like a bud node would)
833 route_3400_eos = VppMplsRoute(self, 3400, 1,
834 [VppRoutePath(self.pg2.remote_ip4,
835 self.pg2.sw_if_index,
837 VppRoutePath(self.pg3.remote_ip4,
838 self.pg3.sw_if_index,
840 VppRoutePath("0.0.0.0",
841 self.pg1.sw_if_index,
844 route_3400_eos.add_vpp_config()
847 # ping an interface in the default table
848 # PG0 is in the default table
850 self.vapi.cli("clear trace")
851 tx = self.create_stream_labelled_ip4(self.pg0, [3400], n=257,
853 self.pg0.add_stream(tx)
855 self.pg_enable_capture(self.pg_interfaces)
858 rx = self.pg1.get_capture(257)
859 self.verify_capture_ip4(self.pg1, rx, tx)
861 rx = self.pg2.get_capture(257)
862 self.verify_capture_labelled(self.pg2, rx, tx, [3401])
863 rx = self.pg3.get_capture(257)
864 self.verify_capture_labelled(self.pg3, rx, tx, [3402])
866 def test_mcast_head(self):
867 """ MPLS Multicast Head-end """
870 # Create a multicast tunnel with two replications
872 mpls_tun = VppMPLSTunnelInterface(self,
873 [VppRoutePath(self.pg2.remote_ip4,
874 self.pg2.sw_if_index,
876 VppRoutePath(self.pg3.remote_ip4,
877 self.pg3.sw_if_index,
880 mpls_tun.add_vpp_config()
884 # add an unlabelled route through the new tunnel
886 route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
887 [VppRoutePath("0.0.0.0",
888 mpls_tun._sw_if_index)])
889 route_10_0_0_3.add_vpp_config()
891 self.vapi.cli("clear trace")
892 tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
893 self.pg0.add_stream(tx)
895 self.pg_enable_capture(self.pg_interfaces)
898 rx = self.pg2.get_capture(257)
899 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [42])
900 rx = self.pg3.get_capture(257)
901 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [43])
904 # An an IP multicast route via the tunnel
906 # one accepting interface, pg0, 1 forwarding interface via the tunnel
908 route_232_1_1_1 = VppIpMRoute(
912 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
913 [VppMRoutePath(self.pg0.sw_if_index,
914 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
915 VppMRoutePath(mpls_tun._sw_if_index,
916 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
917 route_232_1_1_1.add_vpp_config()
919 self.vapi.cli("clear trace")
920 tx = self.create_stream_ip4(self.pg0, "232.1.1.1")
921 self.pg0.add_stream(tx)
923 self.pg_enable_capture(self.pg_interfaces)
926 rx = self.pg2.get_capture(257)
927 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [42])
928 rx = self.pg3.get_capture(257)
929 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [43])
931 def test_mcast_tail(self):
932 """ MPLS Multicast Tail """
935 # Add a multicast route that will forward the traffic
938 route_232_1_1_1 = VppIpMRoute(
942 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
944 paths=[VppMRoutePath(self.pg1.sw_if_index,
945 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
946 route_232_1_1_1.add_vpp_config()
949 # An interface receive label that maps traffic to RX on interface
951 # by injecting the packet in on pg0, which is in table 0
952 # doing an rpf-id and matching a route in table 1
953 # if the packet egresses, then we must have matched the route in
956 route_34_eos = VppMplsRoute(self, 34, 1,
957 [VppRoutePath("0.0.0.0",
958 self.pg1.sw_if_index,
963 route_34_eos.add_vpp_config()
966 # Drop due to interface lookup miss
968 self.vapi.cli("clear trace")
969 tx = self.create_stream_labelled_ip4(self.pg0, [34],
970 dst_ip="232.1.1.1", n=1)
971 self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop none")
974 # set the RPF-ID of the enrtry to match the input packet's
976 route_232_1_1_1.update_rpf_id(55)
978 self.vapi.cli("clear trace")
979 tx = self.create_stream_labelled_ip4(self.pg0, [34],
980 dst_ip="232.1.1.1", n=257)
981 self.pg0.add_stream(tx)
983 self.pg_enable_capture(self.pg_interfaces)
986 rx = self.pg1.get_capture(257)
987 self.verify_capture_ip4(self.pg1, rx, tx)
990 # set the RPF-ID of the enrtry to not match the input packet's
992 route_232_1_1_1.update_rpf_id(56)
993 tx = self.create_stream_labelled_ip4(self.pg0, [34],
995 self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
998 class TestMPLSDisabled(VppTestCase):
999 """ MPLS disabled """
1002 super(TestMPLSDisabled, self).setUp()
1004 # create 2 pg interfaces
1005 self.create_pg_interfaces(range(2))
1007 # PG0 is MPLS enalbed
1009 self.pg0.config_ip4()
1010 self.pg0.resolve_arp()
1011 self.pg0.enable_mpls()
1013 # PG 1 is not MPLS enabled
1017 super(TestMPLSDisabled, self).tearDown()
1018 for i in self.pg_interfaces:
1022 def send_and_assert_no_replies(self, intf, pkts, remark):
1023 intf.add_stream(pkts)
1024 self.pg_enable_capture(self.pg_interfaces)
1026 for i in self.pg_interfaces:
1028 i.assert_nothing_captured(remark=remark)
1030 def test_mpls_disabled(self):
1031 """ MPLS Disabled """
1033 tx = (Ether(src=self.pg1.remote_mac,
1034 dst=self.pg1.local_mac) /
1035 MPLS(label=32, ttl=64) /
1036 IPv6(src="2001::1", dst=self.pg0.remote_ip6) /
1037 UDP(sport=1234, dport=1234) /
1041 # A simple MPLS xconnect - eos label in label out
1043 route_32_eos = VppMplsRoute(self, 32, 1,
1044 [VppRoutePath(self.pg0.remote_ip4,
1045 self.pg0.sw_if_index,
1047 route_32_eos.add_vpp_config()
1050 # PG1 does not forward IP traffic
1052 self.send_and_assert_no_replies(self.pg1, tx, "MPLS disabled")
1057 self.pg1.enable_mpls()
1060 # Now we get packets through
1062 self.pg1.add_stream(tx)
1063 self.pg_enable_capture(self.pg_interfaces)
1066 rx = self.pg0.get_capture(1)
1071 self.pg1.disable_mpls()
1074 # PG1 does not forward IP traffic
1076 self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1077 self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1080 class TestMPLSPIC(VppTestCase):
1081 """ MPLS PIC edge convergence """
1084 super(TestMPLSPIC, self).setUp()
1086 # create 2 pg interfaces
1087 self.create_pg_interfaces(range(4))
1091 self.pg0.config_ip4()
1092 self.pg0.resolve_arp()
1093 self.pg0.enable_mpls()
1095 self.pg1.config_ip4()
1096 self.pg1.resolve_arp()
1097 self.pg1.enable_mpls()
1099 # VRF (customer facing) link
1101 self.pg2.set_table_ip4(1)
1102 self.pg2.config_ip4()
1103 self.pg2.resolve_arp()
1104 self.pg2.set_table_ip6(1)
1105 self.pg2.config_ip6()
1106 self.pg2.resolve_ndp()
1108 self.pg3.set_table_ip4(1)
1109 self.pg3.config_ip4()
1110 self.pg3.resolve_arp()
1111 self.pg3.set_table_ip6(1)
1112 self.pg3.config_ip6()
1113 self.pg3.resolve_ndp()
1116 super(TestMPLSPIC, self).tearDown()
1117 self.pg0.disable_mpls()
1118 for i in self.pg_interfaces:
1125 def test_mpls_ibgp_pic(self):
1126 """ MPLS iBGP PIC edge convergence
1128 1) setup many iBGP VPN routes via a pair of iBGP peers.
1129 2) Check EMCP forwarding to these peers
1130 3) withdraw the IGP route to one of these peers.
1131 4) check forwarding continues to the remaining peer
1135 # IGP+LDP core routes
1137 core_10_0_0_45 = VppIpRoute(self, "10.0.0.45", 32,
1138 [VppRoutePath(self.pg0.remote_ip4,
1139 self.pg0.sw_if_index,
1141 core_10_0_0_45.add_vpp_config()
1143 core_10_0_0_46 = VppIpRoute(self, "10.0.0.46", 32,
1144 [VppRoutePath(self.pg1.remote_ip4,
1145 self.pg1.sw_if_index,
1147 core_10_0_0_46.add_vpp_config()
1150 # Lot's of VPN routes. We need more the 64 so VPP will build
1151 # the fast convergence indirection
1155 for ii in range(64):
1156 dst = "192.168.1.%d" % ii
1157 vpn_routes.append(VppIpRoute(self, dst, 32,
1158 [VppRoutePath("10.0.0.45",
1162 VppRoutePath("10.0.0.46",
1165 is_resolve_host=1)],
1167 vpn_routes[ii].add_vpp_config()
1169 pkts.append(Ether(dst=self.pg2.local_mac,
1170 src=self.pg2.remote_mac) /
1171 IP(src=self.pg2.remote_ip4, dst=dst) /
1172 UDP(sport=1234, dport=1234) /
1176 # Send the packet stream (one pkt to each VPN route)
1177 # - expect a 50-50 split of the traffic
1179 self.pg2.add_stream(pkts)
1180 self.pg_enable_capture(self.pg_interfaces)
1183 rx0 = self.pg0._get_capture(1)
1184 rx1 = self.pg1._get_capture(1)
1186 # not testig the LB hashing algorithm so we're not concerned
1187 # with the split ratio, just as long as neither is 0
1188 self.assertNotEqual(0, len(rx0))
1189 self.assertNotEqual(0, len(rx1))
1192 # use a test CLI command to stop the FIB walk process, this
1193 # will prevent the FIB converging the VPN routes and thus allow
1194 # us to probe the interim (psot-fail, pre-converge) state
1196 self.vapi.ppcli("test fib-walk-process disable")
1199 # Withdraw one of the IGP routes
1201 core_10_0_0_46.remove_vpp_config()
1204 # now all packets should be forwarded through the remaining peer
1206 self.vapi.ppcli("clear trace")
1207 self.pg2.add_stream(pkts)
1208 self.pg_enable_capture(self.pg_interfaces)
1211 rx0 = self.pg0.get_capture(len(pkts))
1214 # enable the FIB walk process to converge the FIB
1216 self.vapi.ppcli("test fib-walk-process enable")
1219 # packets should still be forwarded through the remaining peer
1221 self.pg2.add_stream(pkts)
1222 self.pg_enable_capture(self.pg_interfaces)
1225 rx0 = self.pg0.get_capture(64)
1228 # Add the IGP route back and we return to load-balancing
1230 core_10_0_0_46.add_vpp_config()
1232 self.pg2.add_stream(pkts)
1233 self.pg_enable_capture(self.pg_interfaces)
1236 rx0 = self.pg0._get_capture(1)
1237 rx1 = self.pg1._get_capture(1)
1238 self.assertNotEqual(0, len(rx0))
1239 self.assertNotEqual(0, len(rx1))
1241 def test_mpls_ebgp_pic(self):
1242 """ MPLS eBGP PIC edge convergence
1244 1) setup many eBGP VPN routes via a pair of eBGP peers
1245 2) Check EMCP forwarding to these peers
1246 3) withdraw one eBGP path - expect LB across remaining eBGP
1250 # Lot's of VPN routes. We need more the 64 so VPP will build
1251 # the fast convergence indirection
1256 for ii in range(64):
1257 dst = "192.168.1.%d" % ii
1258 local_label = 1600 + ii
1259 vpn_routes.append(VppIpRoute(self, dst, 32,
1260 [VppRoutePath(self.pg2.remote_ip4,
1263 is_resolve_attached=1),
1264 VppRoutePath(self.pg3.remote_ip4,
1267 is_resolve_attached=1)],
1269 vpn_routes[ii].add_vpp_config()
1271 vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 32,
1273 vpn_bindings[ii].add_vpp_config()
1275 pkts.append(Ether(dst=self.pg0.local_mac,
1276 src=self.pg0.remote_mac) /
1277 MPLS(label=local_label, ttl=64) /
1278 IP(src=self.pg0.remote_ip4, dst=dst) /
1279 UDP(sport=1234, dport=1234) /
1282 self.pg0.add_stream(pkts)
1283 self.pg_enable_capture(self.pg_interfaces)
1286 rx0 = self.pg2._get_capture(1)
1287 rx1 = self.pg3._get_capture(1)
1288 self.assertNotEqual(0, len(rx0))
1289 self.assertNotEqual(0, len(rx1))
1292 # use a test CLI command to stop the FIB walk process, this
1293 # will prevent the FIB converging the VPN routes and thus allow
1294 # us to probe the interim (psot-fail, pre-converge) state
1296 self.vapi.ppcli("test fib-walk-process disable")
1299 # withdraw the connected prefix on the interface.
1301 self.pg2.unconfig_ip4()
1304 # now all packets should be forwarded through the remaining peer
1306 self.pg0.add_stream(pkts)
1307 self.pg_enable_capture(self.pg_interfaces)
1310 rx0 = self.pg3.get_capture(len(pkts))
1313 # enable the FIB walk process to converge the FIB
1315 self.vapi.ppcli("test fib-walk-process enable")
1316 self.pg0.add_stream(pkts)
1317 self.pg_enable_capture(self.pg_interfaces)
1320 rx0 = self.pg3.get_capture(len(pkts))
1323 # put the connecteds back
1325 self.pg2.config_ip4()
1327 self.pg0.add_stream(pkts)
1328 self.pg_enable_capture(self.pg_interfaces)
1331 rx0 = self.pg2._get_capture(1)
1332 rx1 = self.pg3._get_capture(1)
1333 self.assertNotEqual(0, len(rx0))
1334 self.assertNotEqual(0, len(rx1))
1336 def test_mpls_v6_ebgp_pic(self):
1337 """ MPLSv6 eBGP PIC edge convergence
1339 1) setup many eBGP VPNv6 routes via a pair of eBGP peers
1340 2) Check EMCP forwarding to these peers
1341 3) withdraw one eBGP path - expect LB across remaining eBGP
1345 # Lot's of VPN routes. We need more the 64 so VPP will build
1346 # the fast convergence indirection
1351 for ii in range(64):
1352 dst = "3000::%d" % ii
1353 local_label = 1600 + ii
1354 vpn_routes.append(VppIpRoute(self, dst, 128,
1355 [VppRoutePath(self.pg2.remote_ip6,
1358 is_resolve_attached=1,
1360 VppRoutePath(self.pg3.remote_ip6,
1364 is_resolve_attached=1)],
1367 vpn_routes[ii].add_vpp_config()
1369 vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 128,
1372 vpn_bindings[ii].add_vpp_config()
1374 pkts.append(Ether(dst=self.pg0.local_mac,
1375 src=self.pg0.remote_mac) /
1376 MPLS(label=local_label, ttl=64) /
1377 IPv6(src=self.pg0.remote_ip6, dst=dst) /
1378 UDP(sport=1234, dport=1234) /
1381 self.pg0.add_stream(pkts)
1382 self.pg_enable_capture(self.pg_interfaces)
1385 rx0 = self.pg2._get_capture(1)
1386 rx1 = self.pg3._get_capture(1)
1387 self.assertNotEqual(0, len(rx0))
1388 self.assertNotEqual(0, len(rx1))
1391 # use a test CLI command to stop the FIB walk process, this
1392 # will prevent the FIB converging the VPN routes and thus allow
1393 # us to probe the interim (psot-fail, pre-converge) state
1395 self.vapi.ppcli("test fib-walk-process disable")
1398 # withdraw the connected prefix on the interface.
1399 # and shutdown the interface so the ND cache is flushed.
1401 self.pg2.unconfig_ip6()
1402 self.pg2.admin_down()
1405 # now all packets should be forwarded through the remaining peer
1407 self.pg0.add_stream(pkts)
1408 self.pg_enable_capture(self.pg_interfaces)
1411 rx0 = self.pg3.get_capture(len(pkts))
1414 # enable the FIB walk process to converge the FIB
1416 self.vapi.ppcli("test fib-walk-process enable")
1417 self.pg0.add_stream(pkts)
1418 self.pg_enable_capture(self.pg_interfaces)
1421 rx0 = self.pg3.get_capture(len(pkts))
1424 # put the connecteds back
1427 self.pg2.config_ip6()
1429 self.pg0.add_stream(pkts)
1430 self.pg_enable_capture(self.pg_interfaces)
1433 rx0 = self.pg2._get_capture(1)
1434 rx1 = self.pg3._get_capture(1)
1435 self.assertNotEqual(0, len(rx0))
1436 self.assertNotEqual(0, len(rx1))
1439 if __name__ == '__main__':
1440 unittest.main(testRunner=VppTestRunner)