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):
208 capture = self.verify_filter(capture, sent)
210 self.assertEqual(len(capture), len(sent))
212 for i in range(len(capture)):
218 # the MPLS TTL is 255 since it enters a new tunnel
219 self.verify_mpls_stack(
220 rx, mpls_labels, 255, len(mpls_labels) - 1)
222 self.assertEqual(rx_ip.src, tx_ip.src)
223 self.assertEqual(rx_ip.dst, tx_ip.dst)
224 # IP processing post pop has decremented the TTL
225 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
230 def verify_capture_labelled(self, src_if, capture, sent,
231 mpls_labels, ttl=254, num=0):
233 capture = self.verify_filter(capture, sent)
235 self.assertEqual(len(capture), len(sent))
237 for i in range(len(capture)):
239 self.verify_mpls_stack(rx, mpls_labels, ttl, num)
243 def verify_capture_ip6(self, src_if, capture, sent):
245 self.assertEqual(len(capture), len(sent))
247 for i in range(len(capture)):
251 # the rx'd packet has the MPLS label popped
253 self.assertEqual(eth.type, 0x86DD)
258 self.assertEqual(rx_ip.src, tx_ip.src)
259 self.assertEqual(rx_ip.dst, tx_ip.dst)
260 # IP processing post pop has decremented the TTL
261 self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)
266 def send_and_assert_no_replies(self, intf, pkts, remark):
267 intf.add_stream(pkts)
268 self.pg_enable_capture(self.pg_interfaces)
270 for i in self.pg_interfaces:
271 i.assert_nothing_captured(remark=remark)
274 """ MPLS label swap tests """
277 # A simple MPLS xconnect - eos label in label out
279 route_32_eos = VppMplsRoute(self, 32, 1,
280 [VppRoutePath(self.pg0.remote_ip4,
281 self.pg0.sw_if_index,
283 route_32_eos.add_vpp_config()
286 # a stream that matches the route for 10.0.0.1
287 # PG0 is in the default table
289 self.vapi.cli("clear trace")
290 tx = self.create_stream_labelled_ip4(self.pg0, [32])
291 self.pg0.add_stream(tx)
293 self.pg_enable_capture(self.pg_interfaces)
296 rx = self.pg0.get_capture()
297 self.verify_capture_labelled(self.pg0, rx, tx, [33])
300 # A simple MPLS xconnect - non-eos label in label out
302 route_32_neos = VppMplsRoute(self, 32, 0,
303 [VppRoutePath(self.pg0.remote_ip4,
304 self.pg0.sw_if_index,
306 route_32_neos.add_vpp_config()
309 # a stream that matches the route for 10.0.0.1
310 # PG0 is in the default table
312 self.vapi.cli("clear trace")
313 tx = self.create_stream_labelled_ip4(self.pg0, [32, 99])
314 self.pg0.add_stream(tx)
316 self.pg_enable_capture(self.pg_interfaces)
319 rx = self.pg0.get_capture()
320 self.verify_capture_labelled(self.pg0, rx, tx, [33, 99])
323 # An MPLS xconnect - EOS label in IP out
325 route_33_eos = VppMplsRoute(self, 33, 1,
326 [VppRoutePath(self.pg0.remote_ip4,
327 self.pg0.sw_if_index,
329 route_33_eos.add_vpp_config()
331 self.vapi.cli("clear trace")
332 tx = self.create_stream_labelled_ip4(self.pg0, [33])
333 self.pg0.add_stream(tx)
335 self.pg_enable_capture(self.pg_interfaces)
338 rx = self.pg0.get_capture()
339 self.verify_capture_ip4(self.pg0, rx, tx)
342 # An MPLS xconnect - non-EOS label in IP out - an invalid configuration
343 # so this traffic should be dropped.
345 route_33_neos = VppMplsRoute(self, 33, 0,
346 [VppRoutePath(self.pg0.remote_ip4,
347 self.pg0.sw_if_index,
349 route_33_neos.add_vpp_config()
351 self.vapi.cli("clear trace")
352 tx = self.create_stream_labelled_ip4(self.pg0, [33, 99])
353 self.pg0.add_stream(tx)
355 self.pg_enable_capture(self.pg_interfaces)
357 self.pg0.assert_nothing_captured(
358 remark="MPLS non-EOS packets popped and forwarded")
361 # A recursive EOS x-connect, which resolves through another x-connect
363 route_34_eos = VppMplsRoute(self, 34, 1,
364 [VppRoutePath("0.0.0.0",
368 route_34_eos.add_vpp_config()
370 tx = self.create_stream_labelled_ip4(self.pg0, [34])
371 self.pg0.add_stream(tx)
373 self.pg_enable_capture(self.pg_interfaces)
376 rx = self.pg0.get_capture()
377 self.verify_capture_labelled(self.pg0, rx, tx, [33, 44, 45], num=2)
380 # A recursive non-EOS x-connect, which resolves through another
383 route_34_neos = VppMplsRoute(self, 34, 0,
384 [VppRoutePath("0.0.0.0",
388 route_34_neos.add_vpp_config()
390 self.vapi.cli("clear trace")
391 tx = self.create_stream_labelled_ip4(self.pg0, [34, 99])
392 self.pg0.add_stream(tx)
394 self.pg_enable_capture(self.pg_interfaces)
397 rx = self.pg0.get_capture()
398 # it's the 2nd (counting from 0) label in the stack that is swapped
399 self.verify_capture_labelled(self.pg0, rx, tx, [33, 44, 46, 99], num=2)
402 # an recursive IP route that resolves through the recursive non-eos
405 ip_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
406 [VppRoutePath("0.0.0.0",
410 ip_10_0_0_1.add_vpp_config()
412 self.vapi.cli("clear trace")
413 tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
414 self.pg0.add_stream(tx)
416 self.pg_enable_capture(self.pg_interfaces)
419 rx = self.pg0.get_capture()
420 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [33, 44, 46, 55])
422 ip_10_0_0_1.remove_vpp_config()
423 route_34_neos.remove_vpp_config()
424 route_34_eos.remove_vpp_config()
425 route_33_neos.remove_vpp_config()
426 route_33_eos.remove_vpp_config()
427 route_32_neos.remove_vpp_config()
428 route_32_eos.remove_vpp_config()
431 """ MPLS Local Label Binding test """
434 # Add a non-recursive route with a single out label
436 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
437 [VppRoutePath(self.pg0.remote_ip4,
438 self.pg0.sw_if_index,
440 route_10_0_0_1.add_vpp_config()
442 # bind a local label to the route
443 binding = VppMplsIpBind(self, 44, "10.0.0.1", 32)
444 binding.add_vpp_config()
447 self.vapi.cli("clear trace")
448 tx = self.create_stream_labelled_ip4(self.pg0, [44, 99])
449 self.pg0.add_stream(tx)
451 self.pg_enable_capture(self.pg_interfaces)
454 rx = self.pg0.get_capture()
455 self.verify_capture_labelled(self.pg0, rx, tx, [45, 99])
458 self.vapi.cli("clear trace")
459 tx = self.create_stream_labelled_ip4(self.pg0, [44])
460 self.pg0.add_stream(tx)
462 self.pg_enable_capture(self.pg_interfaces)
465 rx = self.pg0.get_capture()
466 self.verify_capture_labelled(self.pg0, rx, tx, [45])
469 self.vapi.cli("clear trace")
470 tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
471 self.pg0.add_stream(tx)
473 self.pg_enable_capture(self.pg_interfaces)
476 rx = self.pg0.get_capture()
477 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [45])
482 binding.remove_vpp_config()
483 route_10_0_0_1.remove_vpp_config()
485 def test_imposition(self):
486 """ MPLS label imposition test """
489 # Add a non-recursive route with a single out label
491 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
492 [VppRoutePath(self.pg0.remote_ip4,
493 self.pg0.sw_if_index,
495 route_10_0_0_1.add_vpp_config()
498 # a stream that matches the route for 10.0.0.1
499 # PG0 is in the default table
501 self.vapi.cli("clear trace")
502 tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
503 self.pg0.add_stream(tx)
505 self.pg_enable_capture(self.pg_interfaces)
508 rx = self.pg0.get_capture()
509 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32])
512 # Add a non-recursive route with a 3 out labels
514 route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
515 [VppRoutePath(self.pg0.remote_ip4,
516 self.pg0.sw_if_index,
517 labels=[32, 33, 34])])
518 route_10_0_0_2.add_vpp_config()
521 # a stream that matches the route for 10.0.0.1
522 # PG0 is in the default table
524 self.vapi.cli("clear trace")
525 tx = self.create_stream_ip4(self.pg0, "10.0.0.2")
526 self.pg0.add_stream(tx)
528 self.pg_enable_capture(self.pg_interfaces)
531 rx = self.pg0.get_capture()
532 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32, 33, 34])
535 # add a recursive path, with output label, via the 1 label route
537 route_11_0_0_1 = VppIpRoute(self, "11.0.0.1", 32,
538 [VppRoutePath("10.0.0.1",
541 route_11_0_0_1.add_vpp_config()
544 # a stream that matches the route for 11.0.0.1, should pick up
545 # the label stack for 11.0.0.1 and 10.0.0.1
547 self.vapi.cli("clear trace")
548 tx = self.create_stream_ip4(self.pg0, "11.0.0.1")
549 self.pg0.add_stream(tx)
551 self.pg_enable_capture(self.pg_interfaces)
554 rx = self.pg0.get_capture()
555 self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32, 44])
558 # add a recursive path, with 2 labels, via the 3 label route
560 route_11_0_0_2 = VppIpRoute(self, "11.0.0.2", 32,
561 [VppRoutePath("10.0.0.2",
564 route_11_0_0_2.add_vpp_config()
567 # a stream that matches the route for 11.0.0.1, should pick up
568 # the label stack for 11.0.0.1 and 10.0.0.1
570 self.vapi.cli("clear trace")
571 tx = self.create_stream_ip4(self.pg0, "11.0.0.2")
572 self.pg0.add_stream(tx)
574 self.pg_enable_capture(self.pg_interfaces)
577 rx = self.pg0.get_capture()
578 self.verify_capture_labelled_ip4(
579 self.pg0, rx, tx, [32, 33, 34, 44, 45])
584 route_11_0_0_2.remove_vpp_config()
585 route_11_0_0_1.remove_vpp_config()
586 route_10_0_0_2.remove_vpp_config()
587 route_10_0_0_1.remove_vpp_config()
589 def test_tunnel(self):
590 """ MPLS Tunnel Tests """
593 # Create a tunnel with a single out label
595 mpls_tun = VppMPLSTunnelInterface(self,
596 [VppRoutePath(self.pg0.remote_ip4,
597 self.pg0.sw_if_index,
599 mpls_tun.add_vpp_config()
603 # add an unlabelled route through the new tunnel
605 route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
606 [VppRoutePath("0.0.0.0",
607 mpls_tun._sw_if_index)])
608 route_10_0_0_3.add_vpp_config()
610 self.vapi.cli("clear trace")
611 tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
612 self.pg0.add_stream(tx)
614 self.pg_enable_capture(self.pg_interfaces)
617 rx = self.pg0.get_capture()
618 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [44, 46])
620 def test_v4_exp_null(self):
621 """ MPLS V4 Explicit NULL test """
624 # The first test case has an MPLS TTL of 0
625 # all packet should be dropped
627 tx = self.create_stream_labelled_ip4(self.pg0, [0], 0)
628 self.pg0.add_stream(tx)
630 self.pg_enable_capture(self.pg_interfaces)
633 self.pg0.assert_nothing_captured(remark="MPLS TTL=0 packets forwarded")
636 # a stream with a non-zero MPLS TTL
637 # PG0 is in the default table
639 tx = self.create_stream_labelled_ip4(self.pg0, [0])
640 self.pg0.add_stream(tx)
642 self.pg_enable_capture(self.pg_interfaces)
645 rx = self.pg0.get_capture()
646 self.verify_capture_ip4(self.pg0, rx, tx)
649 # a stream with a non-zero MPLS TTL
651 # we are ensuring the post-pop lookup occurs in the VRF table
653 self.vapi.cli("clear trace")
654 tx = self.create_stream_labelled_ip4(self.pg1, [0])
655 self.pg1.add_stream(tx)
657 self.pg_enable_capture(self.pg_interfaces)
660 rx = self.pg1.get_capture()
661 self.verify_capture_ip4(self.pg0, rx, tx)
663 def test_v6_exp_null(self):
664 """ MPLS V6 Explicit NULL test """
667 # a stream with a non-zero MPLS TTL
668 # PG0 is in the default table
670 self.vapi.cli("clear trace")
671 tx = self.create_stream_labelled_ip6(self.pg0, 2, 2)
672 self.pg0.add_stream(tx)
674 self.pg_enable_capture(self.pg_interfaces)
677 rx = self.pg0.get_capture()
678 self.verify_capture_ip6(self.pg0, rx, tx)
681 # a stream with a non-zero MPLS TTL
683 # we are ensuring the post-pop lookup occurs in the VRF table
685 self.vapi.cli("clear trace")
686 tx = self.create_stream_labelled_ip6(self.pg1, 2, 2)
687 self.pg1.add_stream(tx)
689 self.pg_enable_capture(self.pg_interfaces)
692 rx = self.pg1.get_capture()
693 self.verify_capture_ip6(self.pg0, rx, tx)
699 # A de-agg route - next-hop lookup in default table
701 route_34_eos = VppMplsRoute(self, 34, 1,
702 [VppRoutePath("0.0.0.0",
705 route_34_eos.add_vpp_config()
708 # ping an interface in the default table
709 # PG0 is in the default table
711 self.vapi.cli("clear trace")
712 tx = self.create_stream_labelled_ip4(self.pg0, [34], ping=1,
714 self.pg0.add_stream(tx)
716 self.pg_enable_capture(self.pg_interfaces)
719 rx = self.pg0.get_capture()
720 self.verify_capture_ip4(self.pg0, rx, tx, ping_resp=1)
723 # A de-agg route - next-hop lookup in non-default table
725 route_35_eos = VppMplsRoute(self, 35, 1,
726 [VppRoutePath("0.0.0.0",
729 route_35_eos.add_vpp_config()
732 # ping an interface in the non-default table
733 # PG0 is in the default table. packet arrive labelled in the
734 # default table and egress unlabelled in the non-default
736 self.vapi.cli("clear trace")
737 tx = self.create_stream_labelled_ip4(
738 self.pg0, [35], ping=1, ip_itf=self.pg1)
739 self.pg0.add_stream(tx)
741 self.pg_enable_capture(self.pg_interfaces)
744 packet_count = self.get_packet_count_for_if_idx(self.pg0.sw_if_index)
745 rx = self.pg1.get_capture(packet_count)
746 self.verify_capture_ip4(self.pg1, rx, tx, ping_resp=1)
748 route_35_eos.remove_vpp_config()
749 route_34_eos.remove_vpp_config()
751 def test_interface_rx(self):
752 """ MPLS Interface Receive """
755 # Add a non-recursive route that will forward the traffic
758 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
760 paths=[VppRoutePath(self.pg1.remote_ip4,
761 self.pg1.sw_if_index)])
762 route_10_0_0_1.add_vpp_config()
765 # An interface receive label that maps traffic to RX on interface
767 # by injecting the packet in on pg0, which is in table 0
768 # doing an interface-rx on pg1 and matching a route in table 1
769 # if the packet egresses, then we must have swapped to pg1
770 # so as to have matched the route in table 1
772 route_34_eos = VppMplsRoute(self, 34, 1,
773 [VppRoutePath("0.0.0.0",
774 self.pg1.sw_if_index,
776 route_34_eos.add_vpp_config()
779 # ping an interface in the default table
780 # PG0 is in the default table
782 self.vapi.cli("clear trace")
783 tx = self.create_stream_labelled_ip4(self.pg0, [34], n=257,
785 self.pg0.add_stream(tx)
787 self.pg_enable_capture(self.pg_interfaces)
790 rx = self.pg1.get_capture(257)
791 self.verify_capture_ip4(self.pg1, rx, tx)
793 def test_mcast_mid_point(self):
794 """ MPLS Multicast Mid Point """
797 # Add a non-recursive route that will forward the traffic
800 route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
802 paths=[VppRoutePath(self.pg1.remote_ip4,
803 self.pg1.sw_if_index)])
804 route_10_0_0_1.add_vpp_config()
807 # Add a mcast entry that replicate to pg2 and pg3
808 # and replicate to a interface-rx (like a bud node would)
810 route_3400_eos = VppMplsRoute(self, 3400, 1,
811 [VppRoutePath(self.pg2.remote_ip4,
812 self.pg2.sw_if_index,
814 VppRoutePath(self.pg3.remote_ip4,
815 self.pg3.sw_if_index,
817 VppRoutePath("0.0.0.0",
818 self.pg1.sw_if_index,
821 route_3400_eos.add_vpp_config()
824 # ping an interface in the default table
825 # PG0 is in the default table
827 self.vapi.cli("clear trace")
828 tx = self.create_stream_labelled_ip4(self.pg0, [3400], n=257,
830 self.pg0.add_stream(tx)
832 self.pg_enable_capture(self.pg_interfaces)
835 rx = self.pg1.get_capture(257)
836 self.verify_capture_ip4(self.pg1, rx, tx)
838 rx = self.pg2.get_capture(257)
839 self.verify_capture_labelled(self.pg2, rx, tx, [3401])
840 rx = self.pg3.get_capture(257)
841 self.verify_capture_labelled(self.pg3, rx, tx, [3402])
843 def test_mcast_head(self):
844 """ MPLS Multicast Head-end """
847 # Create a multicast tunnel with two replications
849 mpls_tun = VppMPLSTunnelInterface(self,
850 [VppRoutePath(self.pg2.remote_ip4,
851 self.pg2.sw_if_index,
853 VppRoutePath(self.pg3.remote_ip4,
854 self.pg3.sw_if_index,
857 mpls_tun.add_vpp_config()
861 # add an unlabelled route through the new tunnel
863 route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
864 [VppRoutePath("0.0.0.0",
865 mpls_tun._sw_if_index)])
866 route_10_0_0_3.add_vpp_config()
868 self.vapi.cli("clear trace")
869 tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
870 self.pg0.add_stream(tx)
872 self.pg_enable_capture(self.pg_interfaces)
875 rx = self.pg2.get_capture(257)
876 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [42])
877 rx = self.pg3.get_capture(257)
878 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [43])
881 # An an IP multicast route via the tunnel
883 # one accepting interface, pg0, 1 forwarding interface via the tunnel
885 route_232_1_1_1 = VppIpMRoute(
889 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
890 [VppMRoutePath(self.pg0.sw_if_index,
891 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
892 VppMRoutePath(mpls_tun._sw_if_index,
893 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
894 route_232_1_1_1.add_vpp_config()
896 self.vapi.cli("clear trace")
897 tx = self.create_stream_ip4(self.pg0, "232.1.1.1")
898 self.pg0.add_stream(tx)
900 self.pg_enable_capture(self.pg_interfaces)
903 rx = self.pg2.get_capture(257)
904 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [42])
905 rx = self.pg3.get_capture(257)
906 self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [43])
908 def test_mcast_tail(self):
909 """ MPLS Multicast Tail """
912 # Add a multicast route that will forward the traffic
915 route_232_1_1_1 = VppIpMRoute(
919 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
921 paths=[VppMRoutePath(self.pg1.sw_if_index,
922 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
923 route_232_1_1_1.add_vpp_config()
926 # An interface receive label that maps traffic to RX on interface
928 # by injecting the packet in on pg0, which is in table 0
929 # doing an rpf-id and matching a route in table 1
930 # if the packet egresses, then we must have matched the route in
933 route_34_eos = VppMplsRoute(self, 34, 1,
934 [VppRoutePath("0.0.0.0",
935 self.pg1.sw_if_index,
940 route_34_eos.add_vpp_config()
943 # Drop due to interface lookup miss
945 self.vapi.cli("clear trace")
946 tx = self.create_stream_labelled_ip4(self.pg0, [34],
947 dst_ip="232.1.1.1", n=1)
948 self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop none")
951 # set the RPF-ID of the enrtry to match the input packet's
953 route_232_1_1_1.update_rpf_id(55)
955 self.vapi.cli("clear trace")
956 tx = self.create_stream_labelled_ip4(self.pg0, [34],
957 dst_ip="232.1.1.1", n=257)
958 self.pg0.add_stream(tx)
960 self.pg_enable_capture(self.pg_interfaces)
963 rx = self.pg1.get_capture(257)
964 self.verify_capture_ip4(self.pg1, rx, tx)
967 # set the RPF-ID of the enrtry to not match the input packet's
969 route_232_1_1_1.update_rpf_id(56)
970 tx = self.create_stream_labelled_ip4(self.pg0, [34],
972 self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
975 class TestMPLSDisabled(VppTestCase):
976 """ MPLS disabled """
979 super(TestMPLSDisabled, self).setUp()
981 # create 2 pg interfaces
982 self.create_pg_interfaces(range(2))
984 # PG0 is MPLS enalbed
986 self.pg0.config_ip4()
987 self.pg0.resolve_arp()
988 self.pg0.enable_mpls()
990 # PG 1 is not MPLS enabled
994 super(TestMPLSDisabled, self).tearDown()
995 for i in self.pg_interfaces:
999 def send_and_assert_no_replies(self, intf, pkts, remark):
1000 intf.add_stream(pkts)
1001 self.pg_enable_capture(self.pg_interfaces)
1003 for i in self.pg_interfaces:
1005 i.assert_nothing_captured(remark=remark)
1007 def test_mpls_disabled(self):
1008 """ MPLS Disabled """
1010 tx = (Ether(src=self.pg1.remote_mac,
1011 dst=self.pg1.local_mac) /
1012 MPLS(label=32, ttl=64) /
1013 IPv6(src="2001::1", dst=self.pg0.remote_ip6) /
1014 UDP(sport=1234, dport=1234) /
1018 # A simple MPLS xconnect - eos label in label out
1020 route_32_eos = VppMplsRoute(self, 32, 1,
1021 [VppRoutePath(self.pg0.remote_ip4,
1022 self.pg0.sw_if_index,
1024 route_32_eos.add_vpp_config()
1027 # PG1 does not forward IP traffic
1029 self.send_and_assert_no_replies(self.pg1, tx, "MPLS disabled")
1034 self.pg1.enable_mpls()
1037 # Now we get packets through
1039 self.pg1.add_stream(tx)
1040 self.pg_enable_capture(self.pg_interfaces)
1043 rx = self.pg0.get_capture(1)
1048 self.pg1.disable_mpls()
1051 # PG1 does not forward IP traffic
1053 self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1054 self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1057 class TestMPLSPIC(VppTestCase):
1058 """ MPLS PIC edge convergence """
1061 super(TestMPLSPIC, self).setUp()
1063 # create 2 pg interfaces
1064 self.create_pg_interfaces(range(4))
1068 self.pg0.config_ip4()
1069 self.pg0.resolve_arp()
1070 self.pg0.enable_mpls()
1072 self.pg1.config_ip4()
1073 self.pg1.resolve_arp()
1074 self.pg1.enable_mpls()
1076 # VRF (customer facing) link
1078 self.pg2.set_table_ip4(1)
1079 self.pg2.config_ip4()
1080 self.pg2.resolve_arp()
1081 self.pg2.set_table_ip6(1)
1082 self.pg2.config_ip6()
1083 self.pg2.resolve_ndp()
1085 self.pg3.set_table_ip4(1)
1086 self.pg3.config_ip4()
1087 self.pg3.resolve_arp()
1088 self.pg3.set_table_ip6(1)
1089 self.pg3.config_ip6()
1090 self.pg3.resolve_ndp()
1093 super(TestMPLSPIC, self).tearDown()
1094 self.pg0.disable_mpls()
1095 for i in self.pg_interfaces:
1102 def test_mpls_ibgp_pic(self):
1103 """ MPLS iBGP PIC edge convergence
1105 1) setup many iBGP VPN routes via a pair of iBGP peers.
1106 2) Check EMCP forwarding to these peers
1107 3) withdraw the IGP route to one of these peers.
1108 4) check forwarding continues to the remaining peer
1112 # IGP+LDP core routes
1114 core_10_0_0_45 = VppIpRoute(self, "10.0.0.45", 32,
1115 [VppRoutePath(self.pg0.remote_ip4,
1116 self.pg0.sw_if_index,
1118 core_10_0_0_45.add_vpp_config()
1120 core_10_0_0_46 = VppIpRoute(self, "10.0.0.46", 32,
1121 [VppRoutePath(self.pg1.remote_ip4,
1122 self.pg1.sw_if_index,
1124 core_10_0_0_46.add_vpp_config()
1127 # Lot's of VPN routes. We need more the 64 so VPP will build
1128 # the fast convergence indirection
1132 for ii in range(64):
1133 dst = "192.168.1.%d" % ii
1134 vpn_routes.append(VppIpRoute(self, dst, 32,
1135 [VppRoutePath("10.0.0.45",
1139 VppRoutePath("10.0.0.46",
1142 is_resolve_host=1)],
1144 vpn_routes[ii].add_vpp_config()
1146 pkts.append(Ether(dst=self.pg2.local_mac,
1147 src=self.pg2.remote_mac) /
1148 IP(src=self.pg2.remote_ip4, dst=dst) /
1149 UDP(sport=1234, dport=1234) /
1153 # Send the packet stream (one pkt to each VPN route)
1154 # - expect a 50-50 split of the traffic
1156 self.pg2.add_stream(pkts)
1157 self.pg_enable_capture(self.pg_interfaces)
1160 rx0 = self.pg0._get_capture(1)
1161 rx1 = self.pg1._get_capture(1)
1163 # not testig the LB hashing algorithm so we're not concerned
1164 # with the split ratio, just as long as neither is 0
1165 self.assertNotEqual(0, len(rx0))
1166 self.assertNotEqual(0, len(rx1))
1169 # use a test CLI command to stop the FIB walk process, this
1170 # will prevent the FIB converging the VPN routes and thus allow
1171 # us to probe the interim (psot-fail, pre-converge) state
1173 self.vapi.ppcli("test fib-walk-process disable")
1176 # Withdraw one of the IGP routes
1178 core_10_0_0_46.remove_vpp_config()
1181 # now all packets should be forwarded through the remaining peer
1183 self.vapi.ppcli("clear trace")
1184 self.pg2.add_stream(pkts)
1185 self.pg_enable_capture(self.pg_interfaces)
1188 rx0 = self.pg0.get_capture(len(pkts))
1191 # enable the FIB walk process to converge the FIB
1193 self.vapi.ppcli("test fib-walk-process enable")
1196 # packets should still be forwarded through the remaining peer
1198 self.pg2.add_stream(pkts)
1199 self.pg_enable_capture(self.pg_interfaces)
1202 rx0 = self.pg0.get_capture(64)
1205 # Add the IGP route back and we return to load-balancing
1207 core_10_0_0_46.add_vpp_config()
1209 self.pg2.add_stream(pkts)
1210 self.pg_enable_capture(self.pg_interfaces)
1213 rx0 = self.pg0._get_capture(1)
1214 rx1 = self.pg1._get_capture(1)
1215 self.assertNotEqual(0, len(rx0))
1216 self.assertNotEqual(0, len(rx1))
1218 def test_mpls_ebgp_pic(self):
1219 """ MPLS eBGP PIC edge convergence
1221 1) setup many eBGP VPN routes via a pair of eBGP peers
1222 2) Check EMCP forwarding to these peers
1223 3) withdraw one eBGP path - expect LB across remaining eBGP
1227 # Lot's of VPN routes. We need more the 64 so VPP will build
1228 # the fast convergence indirection
1233 for ii in range(64):
1234 dst = "192.168.1.%d" % ii
1235 local_label = 1600 + ii
1236 vpn_routes.append(VppIpRoute(self, dst, 32,
1237 [VppRoutePath(self.pg2.remote_ip4,
1240 is_resolve_attached=1),
1241 VppRoutePath(self.pg3.remote_ip4,
1244 is_resolve_attached=1)],
1246 vpn_routes[ii].add_vpp_config()
1248 vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 32,
1250 vpn_bindings[ii].add_vpp_config()
1252 pkts.append(Ether(dst=self.pg0.local_mac,
1253 src=self.pg0.remote_mac) /
1254 MPLS(label=local_label, ttl=64) /
1255 IP(src=self.pg0.remote_ip4, dst=dst) /
1256 UDP(sport=1234, dport=1234) /
1259 self.pg0.add_stream(pkts)
1260 self.pg_enable_capture(self.pg_interfaces)
1263 rx0 = self.pg2._get_capture(1)
1264 rx1 = self.pg3._get_capture(1)
1265 self.assertNotEqual(0, len(rx0))
1266 self.assertNotEqual(0, len(rx1))
1269 # use a test CLI command to stop the FIB walk process, this
1270 # will prevent the FIB converging the VPN routes and thus allow
1271 # us to probe the interim (psot-fail, pre-converge) state
1273 self.vapi.ppcli("test fib-walk-process disable")
1276 # withdraw the connected prefix on the interface.
1278 self.pg2.unconfig_ip4()
1281 # now all packets should be forwarded through the remaining peer
1283 self.pg0.add_stream(pkts)
1284 self.pg_enable_capture(self.pg_interfaces)
1287 rx0 = self.pg3.get_capture(len(pkts))
1290 # enable the FIB walk process to converge the FIB
1292 self.vapi.ppcli("test fib-walk-process enable")
1293 self.pg0.add_stream(pkts)
1294 self.pg_enable_capture(self.pg_interfaces)
1297 rx0 = self.pg3.get_capture(len(pkts))
1300 # put the connecteds back
1302 self.pg2.config_ip4()
1304 self.pg0.add_stream(pkts)
1305 self.pg_enable_capture(self.pg_interfaces)
1308 rx0 = self.pg2._get_capture(1)
1309 rx1 = self.pg3._get_capture(1)
1310 self.assertNotEqual(0, len(rx0))
1311 self.assertNotEqual(0, len(rx1))
1313 def test_mpls_v6_ebgp_pic(self):
1314 """ MPLSv6 eBGP PIC edge convergence
1316 1) setup many eBGP VPNv6 routes via a pair of eBGP peers
1317 2) Check EMCP forwarding to these peers
1318 3) withdraw one eBGP path - expect LB across remaining eBGP
1322 # Lot's of VPN routes. We need more the 64 so VPP will build
1323 # the fast convergence indirection
1328 for ii in range(64):
1329 dst = "3000::%d" % ii
1330 local_label = 1600 + ii
1331 vpn_routes.append(VppIpRoute(self, dst, 128,
1332 [VppRoutePath(self.pg2.remote_ip6,
1335 is_resolve_attached=1,
1337 VppRoutePath(self.pg3.remote_ip6,
1341 is_resolve_attached=1)],
1344 vpn_routes[ii].add_vpp_config()
1346 vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 128,
1349 vpn_bindings[ii].add_vpp_config()
1351 pkts.append(Ether(dst=self.pg0.local_mac,
1352 src=self.pg0.remote_mac) /
1353 MPLS(label=local_label, ttl=64) /
1354 IPv6(src=self.pg0.remote_ip6, dst=dst) /
1355 UDP(sport=1234, dport=1234) /
1358 self.pg0.add_stream(pkts)
1359 self.pg_enable_capture(self.pg_interfaces)
1362 rx0 = self.pg2._get_capture(1)
1363 rx1 = self.pg3._get_capture(1)
1364 self.assertNotEqual(0, len(rx0))
1365 self.assertNotEqual(0, len(rx1))
1368 # use a test CLI command to stop the FIB walk process, this
1369 # will prevent the FIB converging the VPN routes and thus allow
1370 # us to probe the interim (psot-fail, pre-converge) state
1372 self.vapi.ppcli("test fib-walk-process disable")
1375 # withdraw the connected prefix on the interface.
1376 # and shutdown the interface so the ND cache is flushed.
1378 self.pg2.unconfig_ip6()
1379 self.pg2.admin_down()
1382 # now all packets should be forwarded through the remaining peer
1384 self.pg0.add_stream(pkts)
1385 self.pg_enable_capture(self.pg_interfaces)
1388 rx0 = self.pg3.get_capture(len(pkts))
1391 # enable the FIB walk process to converge the FIB
1393 self.vapi.ppcli("test fib-walk-process enable")
1394 self.pg0.add_stream(pkts)
1395 self.pg_enable_capture(self.pg_interfaces)
1398 rx0 = self.pg3.get_capture(len(pkts))
1401 # put the connecteds back
1404 self.pg2.config_ip6()
1406 self.pg0.add_stream(pkts)
1407 self.pg_enable_capture(self.pg_interfaces)
1410 rx0 = self.pg2._get_capture(1)
1411 rx1 = self.pg3._get_capture(1)
1412 self.assertNotEqual(0, len(rx0))
1413 self.assertNotEqual(0, len(rx1))
1416 if __name__ == '__main__':
1417 unittest.main(testRunner=VppTestRunner)