tests: replace pycodestyle with black
[vpp.git] / test / test_mpls.py
1 #!/usr/bin/env python3
2
3 import unittest
4 import socket
5
6 from framework import tag_fixme_vpp_workers
7 from framework import VppTestCase, VppTestRunner
8 from vpp_ip import DpoProto, INVALID_INDEX
9 from vpp_ip_route import (
10     VppIpRoute,
11     VppRoutePath,
12     VppMplsRoute,
13     VppMplsIpBind,
14     VppIpMRoute,
15     VppMRoutePath,
16     VppIpTable,
17     VppMplsTable,
18     VppMplsLabel,
19     MplsLspMode,
20     find_mpls_route,
21     FibPathProto,
22     FibPathType,
23     FibPathFlags,
24     VppMplsLabel,
25     MplsLspMode,
26 )
27 from vpp_mpls_tunnel_interface import VppMPLSTunnelInterface
28 from vpp_papi import VppEnum
29
30 import scapy.compat
31 from scapy.packet import Raw
32 from scapy.layers.l2 import Ether, ARP
33 from scapy.layers.inet import IP, UDP, ICMP, icmptypes, icmpcodes
34 from scapy.layers.inet6 import (
35     IPv6,
36     ICMPv6TimeExceeded,
37     ICMPv6EchoRequest,
38     ICMPv6PacketTooBig,
39 )
40 from scapy.contrib.mpls import MPLS
41
42 NUM_PKTS = 67
43
44 # scapy removed these attributes.
45 # we asked that they be restored: https://github.com/secdev/scapy/pull/1878
46 # semantic names have more meaning than numbers. so here they are.
47 ARP.who_has = 1
48 ARP.is_at = 2
49
50
51 def verify_filter(capture, sent):
52     if not len(capture) == len(sent):
53         # filter out any IPv6 RAs from the capture
54         for p in capture:
55             if p.haslayer(IPv6):
56                 capture.remove(p)
57     return capture
58
59
60 def verify_mpls_stack(tst, rx, mpls_labels):
61     # the rx'd packet has the MPLS label popped
62     eth = rx[Ether]
63     tst.assertEqual(eth.type, 0x8847)
64
65     rx_mpls = rx[MPLS]
66
67     for ii in range(len(mpls_labels)):
68         tst.assertEqual(rx_mpls.label, mpls_labels[ii].value)
69         tst.assertEqual(rx_mpls.cos, mpls_labels[ii].exp)
70         tst.assertEqual(rx_mpls.ttl, mpls_labels[ii].ttl)
71
72         if ii == len(mpls_labels) - 1:
73             tst.assertEqual(rx_mpls.s, 1)
74         else:
75             # not end of stack
76             tst.assertEqual(rx_mpls.s, 0)
77             # pop the label to expose the next
78             rx_mpls = rx_mpls[MPLS].payload
79
80
81 @tag_fixme_vpp_workers
82 class TestMPLS(VppTestCase):
83     """MPLS Test Case"""
84
85     @classmethod
86     def setUpClass(cls):
87         super(TestMPLS, cls).setUpClass()
88
89     @classmethod
90     def tearDownClass(cls):
91         super(TestMPLS, cls).tearDownClass()
92
93     def setUp(self):
94         super(TestMPLS, self).setUp()
95
96         # create 2 pg interfaces
97         self.create_pg_interfaces(range(4))
98
99         # setup both interfaces
100         # assign them different tables.
101         table_id = 0
102         self.tables = []
103
104         tbl = VppMplsTable(self, 0)
105         tbl.add_vpp_config()
106         self.tables.append(tbl)
107
108         for i in self.pg_interfaces:
109             i.admin_up()
110
111             if table_id != 0:
112                 tbl = VppIpTable(self, table_id)
113                 tbl.add_vpp_config()
114                 self.tables.append(tbl)
115                 tbl = VppIpTable(self, table_id, is_ip6=1)
116                 tbl.add_vpp_config()
117                 self.tables.append(tbl)
118
119             i.set_table_ip4(table_id)
120             i.set_table_ip6(table_id)
121             i.config_ip4()
122             i.resolve_arp()
123             i.config_ip6()
124             i.resolve_ndp()
125             i.enable_mpls()
126             table_id += 1
127
128     def tearDown(self):
129         for i in self.pg_interfaces:
130             i.unconfig_ip4()
131             i.unconfig_ip6()
132             i.set_table_ip4(0)
133             i.set_table_ip6(0)
134             i.disable_mpls()
135             i.admin_down()
136         super(TestMPLS, self).tearDown()
137
138     # the default of 64 matches the IP packet TTL default
139     def create_stream_labelled_ip4(
140         self,
141         src_if,
142         mpls_labels,
143         ping=0,
144         ip_itf=None,
145         dst_ip=None,
146         chksum=None,
147         ip_ttl=64,
148         n=257,
149     ):
150         self.reset_packet_infos()
151         pkts = []
152         for i in range(0, n):
153             info = self.create_packet_info(src_if, src_if)
154             payload = self.info_to_payload(info)
155             p = Ether(dst=src_if.local_mac, src=src_if.remote_mac)
156
157             for ii in range(len(mpls_labels)):
158                 p = p / MPLS(
159                     label=mpls_labels[ii].value,
160                     ttl=mpls_labels[ii].ttl,
161                     cos=mpls_labels[ii].exp,
162                 )
163             if not ping:
164                 if not dst_ip:
165                     p = (
166                         p
167                         / IP(src=src_if.local_ip4, dst=src_if.remote_ip4, ttl=ip_ttl)
168                         / UDP(sport=1234, dport=1234)
169                         / Raw(payload)
170                     )
171                 else:
172                     p = (
173                         p
174                         / IP(src=src_if.local_ip4, dst=dst_ip, ttl=ip_ttl)
175                         / UDP(sport=1234, dport=1234)
176                         / Raw(payload)
177                     )
178             else:
179                 p = (
180                     p
181                     / IP(src=ip_itf.remote_ip4, dst=ip_itf.local_ip4, ttl=ip_ttl)
182                     / ICMP()
183                 )
184
185             if chksum:
186                 p[IP].chksum = chksum
187             info.data = p.copy()
188             pkts.append(p)
189         return pkts
190
191     def create_stream_ip4(
192         self, src_if, dst_ip, ip_ttl=64, ip_dscp=0, payload_size=None
193     ):
194         self.reset_packet_infos()
195         pkts = []
196         for i in range(0, 257):
197             info = self.create_packet_info(src_if, src_if)
198             payload = self.info_to_payload(info)
199             p = (
200                 Ether(dst=src_if.local_mac, src=src_if.remote_mac)
201                 / IP(src=src_if.remote_ip4, dst=dst_ip, ttl=ip_ttl, tos=ip_dscp)
202                 / UDP(sport=1234, dport=1234)
203                 / Raw(payload)
204             )
205             info.data = p.copy()
206             if payload_size:
207                 self.extend_packet(p, payload_size)
208             pkts.append(p)
209         return pkts
210
211     def create_stream_ip6(self, src_if, dst_ip, ip_ttl=64, ip_dscp=0):
212         self.reset_packet_infos()
213         pkts = []
214         for i in range(0, 257):
215             info = self.create_packet_info(src_if, src_if)
216             payload = self.info_to_payload(info)
217             p = (
218                 Ether(dst=src_if.local_mac, src=src_if.remote_mac)
219                 / IPv6(src=src_if.remote_ip6, dst=dst_ip, hlim=ip_ttl, tc=ip_dscp)
220                 / UDP(sport=1234, dport=1234)
221                 / Raw(payload)
222             )
223             info.data = p.copy()
224             pkts.append(p)
225         return pkts
226
227     def create_stream_labelled_ip6(
228         self, src_if, mpls_labels, hlim=64, dst_ip=None, ping=0, ip_itf=None
229     ):
230         if dst_ip is None:
231             dst_ip = src_if.remote_ip6
232         self.reset_packet_infos()
233         pkts = []
234         for i in range(0, 257):
235             info = self.create_packet_info(src_if, src_if)
236             payload = self.info_to_payload(info)
237             p = Ether(dst=src_if.local_mac, src=src_if.remote_mac)
238             for l in mpls_labels:
239                 p = p / MPLS(label=l.value, ttl=l.ttl, cos=l.exp)
240
241             if ping:
242                 p = p / (
243                     IPv6(src=ip_itf.remote_ip6, dst=ip_itf.local_ip6)
244                     / ICMPv6EchoRequest()
245                 )
246             else:
247                 p = p / (
248                     IPv6(src=src_if.remote_ip6, dst=dst_ip, hlim=hlim)
249                     / UDP(sport=1234, dport=1234)
250                     / Raw(payload)
251                 )
252             info.data = p.copy()
253             pkts.append(p)
254         return pkts
255
256     def verify_capture_ip4(
257         self, src_if, capture, sent, ping_resp=0, ip_ttl=None, ip_dscp=0
258     ):
259         try:
260             capture = verify_filter(capture, sent)
261
262             self.assertEqual(len(capture), len(sent))
263
264             for i in range(len(capture)):
265                 tx = sent[i]
266                 rx = capture[i]
267
268                 # the rx'd packet has the MPLS label popped
269                 eth = rx[Ether]
270                 self.assertEqual(eth.type, 0x800)
271
272                 tx_ip = tx[IP]
273                 rx_ip = rx[IP]
274
275                 if not ping_resp:
276                     self.assertEqual(rx_ip.src, tx_ip.src)
277                     self.assertEqual(rx_ip.dst, tx_ip.dst)
278                     self.assertEqual(rx_ip.tos, ip_dscp)
279                     if not ip_ttl:
280                         # IP processing post pop has decremented the TTL
281                         self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
282                     else:
283                         self.assertEqual(rx_ip.ttl, ip_ttl)
284                 else:
285                     self.assertEqual(rx_ip.src, tx_ip.dst)
286                     self.assertEqual(rx_ip.dst, tx_ip.src)
287
288         except:
289             raise
290
291     def verify_capture_labelled_ip4(
292         self, src_if, capture, sent, mpls_labels, ip_ttl=None
293     ):
294         try:
295             capture = verify_filter(capture, sent)
296
297             self.assertEqual(len(capture), len(sent))
298
299             for i in range(len(capture)):
300                 tx = sent[i]
301                 rx = capture[i]
302                 tx_ip = tx[IP]
303                 rx_ip = rx[IP]
304
305                 verify_mpls_stack(self, rx, mpls_labels)
306
307                 self.assertEqual(rx_ip.src, tx_ip.src)
308                 self.assertEqual(rx_ip.dst, tx_ip.dst)
309                 if not ip_ttl:
310                     # IP processing post pop has decremented the TTL
311                     self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
312                 else:
313                     self.assertEqual(rx_ip.ttl, ip_ttl)
314
315         except:
316             raise
317
318     def verify_capture_labelled_ip6(
319         self, src_if, capture, sent, mpls_labels, ip_ttl=None
320     ):
321         try:
322             capture = verify_filter(capture, sent)
323
324             self.assertEqual(len(capture), len(sent))
325
326             for i in range(len(capture)):
327                 tx = sent[i]
328                 rx = capture[i]
329                 tx_ip = tx[IPv6]
330                 rx_ip = rx[IPv6]
331
332                 verify_mpls_stack(self, rx, mpls_labels)
333
334                 self.assertEqual(rx_ip.src, tx_ip.src)
335                 self.assertEqual(rx_ip.dst, tx_ip.dst)
336                 if not ip_ttl:
337                     # IP processing post pop has decremented the TTL
338                     self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)
339                 else:
340                     self.assertEqual(rx_ip.hlim, ip_ttl)
341
342         except:
343             raise
344
345     def verify_capture_tunneled_ip4(self, src_if, capture, sent, mpls_labels):
346         try:
347             capture = verify_filter(capture, sent)
348
349             self.assertEqual(len(capture), len(sent))
350
351             for i in range(len(capture)):
352                 tx = sent[i]
353                 rx = capture[i]
354                 tx_ip = tx[IP]
355                 rx_ip = rx[IP]
356
357                 verify_mpls_stack(self, rx, mpls_labels)
358
359                 self.assertEqual(rx_ip.src, tx_ip.src)
360                 self.assertEqual(rx_ip.dst, tx_ip.dst)
361                 # IP processing post pop has decremented the TTL
362                 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
363
364         except:
365             raise
366
367     def verify_capture_labelled(self, src_if, capture, sent, mpls_labels):
368         try:
369             capture = verify_filter(capture, sent)
370
371             self.assertEqual(len(capture), len(sent))
372
373             for i in range(len(capture)):
374                 rx = capture[i]
375                 verify_mpls_stack(self, rx, mpls_labels)
376         except:
377             raise
378
379     def verify_capture_ip6(
380         self, src_if, capture, sent, ip_hlim=None, ip_dscp=0, ping_resp=0
381     ):
382         try:
383             self.assertEqual(len(capture), len(sent))
384
385             for i in range(len(capture)):
386                 tx = sent[i]
387                 rx = capture[i]
388
389                 # the rx'd packet has the MPLS label popped
390                 eth = rx[Ether]
391                 self.assertEqual(eth.type, 0x86DD)
392
393                 tx_ip = tx[IPv6]
394                 rx_ip = rx[IPv6]
395
396                 if not ping_resp:
397                     self.assertEqual(rx_ip.src, tx_ip.src)
398                     self.assertEqual(rx_ip.dst, tx_ip.dst)
399                     self.assertEqual(rx_ip.tc, ip_dscp)
400                     # IP processing post pop has decremented the TTL
401                     if not ip_hlim:
402                         self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)
403                     else:
404                         self.assertEqual(rx_ip.hlim, ip_hlim)
405                 else:
406                     self.assertEqual(rx_ip.src, tx_ip.dst)
407                     self.assertEqual(rx_ip.dst, tx_ip.src)
408         except:
409             raise
410
411     def verify_capture_ip6_icmp(self, src_if, capture, sent):
412         try:
413             # rate limited ICMP
414             self.assertTrue(len(capture) <= len(sent))
415
416             for i in range(len(capture)):
417                 tx = sent[i]
418                 rx = capture[i]
419
420                 # the rx'd packet has the MPLS label popped
421                 eth = rx[Ether]
422                 self.assertEqual(eth.type, 0x86DD)
423
424                 tx_ip = tx[IPv6]
425                 rx_ip = rx[IPv6]
426
427                 self.assertEqual(rx_ip.dst, tx_ip.src)
428                 # ICMP sourced from the interface's address
429                 self.assertEqual(rx_ip.src, src_if.local_ip6)
430                 # hop-limit reset to 255 for IMCP packet
431                 self.assertEqual(rx_ip.hlim, 255)
432
433                 icmp = rx[ICMPv6TimeExceeded]
434
435         except:
436             raise
437
438     def verify_capture_fragmented_labelled_ip4(
439         self, src_if, capture, sent, mpls_labels, ip_ttl=None
440     ):
441         try:
442             capture = verify_filter(capture, sent)
443
444             for i in range(len(capture)):
445                 tx = sent[0]
446                 rx = capture[i]
447                 tx_ip = tx[IP]
448                 rx_ip = rx[IP]
449
450                 verify_mpls_stack(self, rx, mpls_labels)
451
452                 self.assertEqual(rx_ip.src, tx_ip.src)
453                 self.assertEqual(rx_ip.dst, tx_ip.dst)
454                 if not ip_ttl:
455                     # IP processing post pop has decremented the TTL
456                     self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
457                 else:
458                     self.assertEqual(rx_ip.ttl, ip_ttl)
459
460         except:
461             raise
462
463     def verify_capture_fragmented_labelled_ip6(
464         self, src_if, capture, sent, mpls_labels, ip_ttl=None
465     ):
466         try:
467             capture = verify_filter(capture, sent)
468
469             for i in range(len(capture)):
470                 tx = sent[0]
471                 rx = capture[i]
472                 tx_ip = tx[IPv6]
473                 rx.show()
474                 rx_ip = IPv6(rx[MPLS].payload)
475                 rx_ip.show()
476
477                 verify_mpls_stack(self, rx, mpls_labels)
478
479                 self.assertEqual(rx_ip.src, tx_ip.src)
480                 self.assertEqual(rx_ip.dst, tx_ip.dst)
481                 if not ip_ttl:
482                     # IP processing post pop has decremented the hop-limit
483                     self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)
484                 else:
485                     self.assertEqual(rx_ip.hlim, ip_ttl)
486         except:
487             raise
488
489     def test_swap(self):
490         """MPLS label swap tests"""
491
492         #
493         # A simple MPLS xconnect - eos label in label out
494         #
495         route_32_eos = VppMplsRoute(
496             self,
497             32,
498             1,
499             [
500                 VppRoutePath(
501                     self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[VppMplsLabel(33)]
502                 )
503             ],
504         )
505         route_32_eos.add_vpp_config()
506
507         self.assertTrue(
508             find_mpls_route(
509                 self,
510                 0,
511                 32,
512                 1,
513                 [
514                     VppRoutePath(
515                         self.pg0.remote_ip4,
516                         self.pg0.sw_if_index,
517                         labels=[VppMplsLabel(33)],
518                     )
519                 ],
520             )
521         )
522
523         #
524         # a stream that matches the route for 10.0.0.1
525         # PG0 is in the default table
526         #
527         tx = self.create_stream_labelled_ip4(
528             self.pg0, [VppMplsLabel(32, ttl=32, exp=1)]
529         )
530         rx = self.send_and_expect(self.pg0, tx, self.pg0)
531         self.verify_capture_labelled(
532             self.pg0, rx, tx, [VppMplsLabel(33, ttl=31, exp=1)]
533         )
534
535         self.assertEqual(route_32_eos.get_stats_to()["packets"], 257)
536
537         #
538         # A simple MPLS xconnect - non-eos label in label out
539         #
540         route_32_neos = VppMplsRoute(
541             self,
542             32,
543             0,
544             [
545                 VppRoutePath(
546                     self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[VppMplsLabel(33)]
547                 )
548             ],
549         )
550         route_32_neos.add_vpp_config()
551
552         #
553         # a stream that matches the route for 10.0.0.1
554         # PG0 is in the default table
555         #
556         tx = self.create_stream_labelled_ip4(
557             self.pg0, [VppMplsLabel(32, ttl=21, exp=7), VppMplsLabel(99)]
558         )
559         rx = self.send_and_expect(self.pg0, tx, self.pg0)
560         self.verify_capture_labelled(
561             self.pg0, rx, tx, [VppMplsLabel(33, ttl=20, exp=7), VppMplsLabel(99)]
562         )
563         self.assertEqual(route_32_neos.get_stats_to()["packets"], 257)
564
565         #
566         # A simple MPLS xconnect - non-eos label in label out, uniform mode
567         #
568         route_42_neos = VppMplsRoute(
569             self,
570             42,
571             0,
572             [
573                 VppRoutePath(
574                     self.pg0.remote_ip4,
575                     self.pg0.sw_if_index,
576                     labels=[VppMplsLabel(43, MplsLspMode.UNIFORM)],
577                 )
578             ],
579         )
580         route_42_neos.add_vpp_config()
581
582         tx = self.create_stream_labelled_ip4(
583             self.pg0, [VppMplsLabel(42, ttl=21, exp=7), VppMplsLabel(99)]
584         )
585         rx = self.send_and_expect(self.pg0, tx, self.pg0)
586         self.verify_capture_labelled(
587             self.pg0, rx, tx, [VppMplsLabel(43, ttl=20, exp=7), VppMplsLabel(99)]
588         )
589
590         #
591         # An MPLS xconnect - EOS label in IP out
592         #
593         route_33_eos = VppMplsRoute(
594             self,
595             33,
596             1,
597             [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[])],
598         )
599         route_33_eos.add_vpp_config()
600
601         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(33)])
602         rx = self.send_and_expect(self.pg0, tx, self.pg0)
603         self.verify_capture_ip4(self.pg0, rx, tx)
604
605         #
606         # disposed packets have an invalid IPv4 checksum
607         #
608         tx = self.create_stream_labelled_ip4(
609             self.pg0, [VppMplsLabel(33)], dst_ip=self.pg0.remote_ip4, n=65, chksum=1
610         )
611         self.send_and_assert_no_replies(self.pg0, tx, "Invalid Checksum")
612
613         #
614         # An MPLS xconnect - EOS label in IP out, uniform mode
615         #
616         route_3333_eos = VppMplsRoute(
617             self,
618             3333,
619             1,
620             [
621                 VppRoutePath(
622                     self.pg0.remote_ip4,
623                     self.pg0.sw_if_index,
624                     labels=[VppMplsLabel(3, MplsLspMode.UNIFORM)],
625                 )
626             ],
627         )
628         route_3333_eos.add_vpp_config()
629
630         tx = self.create_stream_labelled_ip4(
631             self.pg0, [VppMplsLabel(3333, ttl=55, exp=3)]
632         )
633         rx = self.send_and_expect(self.pg0, tx, self.pg0)
634         self.verify_capture_ip4(self.pg0, rx, tx, ip_ttl=54, ip_dscp=0x60)
635         tx = self.create_stream_labelled_ip4(
636             self.pg0, [VppMplsLabel(3333, ttl=66, exp=4)]
637         )
638         rx = self.send_and_expect(self.pg0, tx, self.pg0)
639         self.verify_capture_ip4(self.pg0, rx, tx, ip_ttl=65, ip_dscp=0x80)
640
641         #
642         # An MPLS xconnect - EOS label in IPv6 out
643         #
644         route_333_eos = VppMplsRoute(
645             self,
646             333,
647             1,
648             [VppRoutePath(self.pg0.remote_ip6, self.pg0.sw_if_index, labels=[])],
649             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
650         )
651         route_333_eos.add_vpp_config()
652
653         tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(333)])
654         rx = self.send_and_expect(self.pg0, tx, self.pg0)
655         self.verify_capture_ip6(self.pg0, rx, tx)
656
657         #
658         # disposed packets have an TTL expired
659         #
660         tx = self.create_stream_labelled_ip6(
661             self.pg0, [VppMplsLabel(333, ttl=64)], dst_ip=self.pg1.remote_ip6, hlim=1
662         )
663         rx = self.send_and_expect_some(self.pg0, tx, self.pg0)
664         self.verify_capture_ip6_icmp(self.pg0, rx, tx)
665
666         #
667         # An MPLS xconnect - EOS label in IPv6 out w imp-null
668         #
669         route_334_eos = VppMplsRoute(
670             self,
671             334,
672             1,
673             [
674                 VppRoutePath(
675                     self.pg0.remote_ip6, self.pg0.sw_if_index, labels=[VppMplsLabel(3)]
676                 )
677             ],
678             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
679         )
680         route_334_eos.add_vpp_config()
681
682         tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(334, ttl=64)])
683         rx = self.send_and_expect(self.pg0, tx, self.pg0)
684         self.verify_capture_ip6(self.pg0, rx, tx)
685
686         #
687         # An MPLS xconnect - EOS label in IPv6 out w imp-null in uniform mode
688         #
689         route_335_eos = VppMplsRoute(
690             self,
691             335,
692             1,
693             [
694                 VppRoutePath(
695                     self.pg0.remote_ip6,
696                     self.pg0.sw_if_index,
697                     labels=[VppMplsLabel(3, MplsLspMode.UNIFORM)],
698                 )
699             ],
700             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
701         )
702         route_335_eos.add_vpp_config()
703
704         tx = self.create_stream_labelled_ip6(
705             self.pg0, [VppMplsLabel(335, ttl=27, exp=4)]
706         )
707         rx = self.send_and_expect(self.pg0, tx, self.pg0)
708         self.verify_capture_ip6(self.pg0, rx, tx, ip_hlim=26, ip_dscp=0x80)
709
710         #
711         # disposed packets have an TTL expired
712         #
713         tx = self.create_stream_labelled_ip6(
714             self.pg0, [VppMplsLabel(334)], dst_ip=self.pg1.remote_ip6, hlim=0
715         )
716         rx = self.send_and_expect_some(self.pg0, tx, self.pg0)
717         self.verify_capture_ip6_icmp(self.pg0, rx, tx)
718
719         #
720         # An MPLS xconnect - non-EOS label in IP out - an invalid configuration
721         # so this traffic should be dropped.
722         #
723         route_33_neos = VppMplsRoute(
724             self,
725             33,
726             0,
727             [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[])],
728         )
729         route_33_neos.add_vpp_config()
730
731         tx = self.create_stream_labelled_ip4(
732             self.pg0, [VppMplsLabel(33), VppMplsLabel(99)]
733         )
734         self.send_and_assert_no_replies(
735             self.pg0, tx, "MPLS non-EOS packets popped and forwarded"
736         )
737
738         #
739         # A recursive EOS x-connect, which resolves through another x-connect
740         # in pipe mode
741         #
742         route_34_eos = VppMplsRoute(
743             self,
744             34,
745             1,
746             [
747                 VppRoutePath(
748                     "0.0.0.0",
749                     0xFFFFFFFF,
750                     nh_via_label=32,
751                     labels=[VppMplsLabel(44), VppMplsLabel(45)],
752                 )
753             ],
754         )
755         route_34_eos.add_vpp_config()
756         self.logger.info(self.vapi.cli("sh mpls fib 34"))
757
758         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(34, ttl=3)])
759         rx = self.send_and_expect(self.pg0, tx, self.pg0)
760         self.verify_capture_labelled(
761             self.pg0,
762             rx,
763             tx,
764             [VppMplsLabel(33), VppMplsLabel(44), VppMplsLabel(45, ttl=2)],
765         )
766
767         self.assertEqual(route_34_eos.get_stats_to()["packets"], 257)
768         self.assertEqual(route_32_neos.get_stats_via()["packets"], 257)
769
770         #
771         # A recursive EOS x-connect, which resolves through another x-connect
772         # in uniform mode
773         #
774         route_35_eos = VppMplsRoute(
775             self,
776             35,
777             1,
778             [
779                 VppRoutePath(
780                     "0.0.0.0", 0xFFFFFFFF, nh_via_label=42, labels=[VppMplsLabel(44)]
781                 )
782             ],
783         )
784         route_35_eos.add_vpp_config()
785
786         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(35, ttl=3)])
787         rx = self.send_and_expect(self.pg0, tx, self.pg0)
788         self.verify_capture_labelled(
789             self.pg0, rx, tx, [VppMplsLabel(43, ttl=2), VppMplsLabel(44, ttl=2)]
790         )
791
792         #
793         # A recursive non-EOS x-connect, which resolves through another
794         # x-connect
795         #
796         route_34_neos = VppMplsRoute(
797             self,
798             34,
799             0,
800             [
801                 VppRoutePath(
802                     "0.0.0.0",
803                     0xFFFFFFFF,
804                     nh_via_label=32,
805                     labels=[VppMplsLabel(44), VppMplsLabel(46)],
806                 )
807             ],
808         )
809         route_34_neos.add_vpp_config()
810
811         tx = self.create_stream_labelled_ip4(
812             self.pg0, [VppMplsLabel(34, ttl=45), VppMplsLabel(99)]
813         )
814         rx = self.send_and_expect(self.pg0, tx, self.pg0)
815         # it's the 2nd (counting from 0) label in the stack that is swapped
816         self.verify_capture_labelled(
817             self.pg0,
818             rx,
819             tx,
820             [
821                 VppMplsLabel(33),
822                 VppMplsLabel(44),
823                 VppMplsLabel(46, ttl=44),
824                 VppMplsLabel(99),
825             ],
826         )
827
828         #
829         # an recursive IP route that resolves through the recursive non-eos
830         # x-connect
831         #
832         ip_10_0_0_1 = VppIpRoute(
833             self,
834             "10.0.0.1",
835             32,
836             [
837                 VppRoutePath(
838                     "0.0.0.0", 0xFFFFFFFF, nh_via_label=34, labels=[VppMplsLabel(55)]
839                 )
840             ],
841         )
842         ip_10_0_0_1.add_vpp_config()
843
844         tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
845         rx = self.send_and_expect(self.pg0, tx, self.pg0)
846         self.verify_capture_labelled_ip4(
847             self.pg0,
848             rx,
849             tx,
850             [VppMplsLabel(33), VppMplsLabel(44), VppMplsLabel(46), VppMplsLabel(55)],
851         )
852         self.assertEqual(ip_10_0_0_1.get_stats_to()["packets"], 257)
853
854         ip_10_0_0_1.remove_vpp_config()
855         route_34_neos.remove_vpp_config()
856         route_34_eos.remove_vpp_config()
857         route_33_neos.remove_vpp_config()
858         route_33_eos.remove_vpp_config()
859         route_32_neos.remove_vpp_config()
860         route_32_eos.remove_vpp_config()
861
862     def test_bind(self):
863         """MPLS Local Label Binding test"""
864
865         #
866         # Add a non-recursive route with a single out label
867         #
868         route_10_0_0_1 = VppIpRoute(
869             self,
870             "10.0.0.1",
871             32,
872             [
873                 VppRoutePath(
874                     self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[VppMplsLabel(45)]
875                 )
876             ],
877         )
878         route_10_0_0_1.add_vpp_config()
879
880         # bind a local label to the route
881         binding = VppMplsIpBind(self, 44, "10.0.0.1", 32)
882         binding.add_vpp_config()
883
884         # non-EOS stream
885         tx = self.create_stream_labelled_ip4(
886             self.pg0, [VppMplsLabel(44), VppMplsLabel(99)]
887         )
888         rx = self.send_and_expect(self.pg0, tx, self.pg0)
889         self.verify_capture_labelled(
890             self.pg0, rx, tx, [VppMplsLabel(45, ttl=63), VppMplsLabel(99)]
891         )
892
893         # EOS stream
894         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(44)])
895         rx = self.send_and_expect(self.pg0, tx, self.pg0)
896         self.verify_capture_labelled(self.pg0, rx, tx, [VppMplsLabel(45, ttl=63)])
897
898         # IP stream
899         tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
900         rx = self.send_and_expect(self.pg0, tx, self.pg0)
901         self.verify_capture_labelled_ip4(self.pg0, rx, tx, [VppMplsLabel(45)])
902
903         #
904         # cleanup
905         #
906         binding.remove_vpp_config()
907         route_10_0_0_1.remove_vpp_config()
908
909     def test_imposition(self):
910         """MPLS label imposition test"""
911
912         #
913         # Add a non-recursive route with a single out label
914         #
915         route_10_0_0_1 = VppIpRoute(
916             self,
917             "10.0.0.1",
918             32,
919             [
920                 VppRoutePath(
921                     self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[VppMplsLabel(32)]
922                 )
923             ],
924         )
925         route_10_0_0_1.add_vpp_config()
926
927         #
928         # a stream that matches the route for 10.0.0.1
929         # PG0 is in the default table
930         #
931         tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
932         rx = self.send_and_expect(self.pg0, tx, self.pg0)
933         self.verify_capture_labelled_ip4(self.pg0, rx, tx, [VppMplsLabel(32)])
934
935         #
936         # Add a non-recursive route with a 3 out labels
937         #
938         route_10_0_0_2 = VppIpRoute(
939             self,
940             "10.0.0.2",
941             32,
942             [
943                 VppRoutePath(
944                     self.pg0.remote_ip4,
945                     self.pg0.sw_if_index,
946                     labels=[VppMplsLabel(32), VppMplsLabel(33), VppMplsLabel(34)],
947                 )
948             ],
949         )
950         route_10_0_0_2.add_vpp_config()
951
952         tx = self.create_stream_ip4(self.pg0, "10.0.0.2", ip_ttl=44, ip_dscp=0xFF)
953         rx = self.send_and_expect(self.pg0, tx, self.pg0)
954         self.verify_capture_labelled_ip4(
955             self.pg0,
956             rx,
957             tx,
958             [VppMplsLabel(32), VppMplsLabel(33), VppMplsLabel(34)],
959             ip_ttl=43,
960         )
961
962         #
963         # Add a non-recursive route with a single out label in uniform mode
964         #
965         route_10_0_0_3 = VppIpRoute(
966             self,
967             "10.0.0.3",
968             32,
969             [
970                 VppRoutePath(
971                     self.pg0.remote_ip4,
972                     self.pg0.sw_if_index,
973                     labels=[VppMplsLabel(32, mode=MplsLspMode.UNIFORM)],
974                 )
975             ],
976         )
977         route_10_0_0_3.add_vpp_config()
978
979         tx = self.create_stream_ip4(self.pg0, "10.0.0.3", ip_ttl=54, ip_dscp=0xBE)
980         rx = self.send_and_expect(self.pg0, tx, self.pg0)
981         self.verify_capture_labelled_ip4(
982             self.pg0, rx, tx, [VppMplsLabel(32, ttl=53, exp=5)]
983         )
984
985         #
986         # Add a IPv6 non-recursive route with a single out label in
987         # uniform mode
988         #
989         route_2001_3 = VppIpRoute(
990             self,
991             "2001::3",
992             128,
993             [
994                 VppRoutePath(
995                     self.pg0.remote_ip6,
996                     self.pg0.sw_if_index,
997                     labels=[VppMplsLabel(32, mode=MplsLspMode.UNIFORM)],
998                 )
999             ],
1000         )
1001         route_2001_3.add_vpp_config()
1002
1003         tx = self.create_stream_ip6(self.pg0, "2001::3", ip_ttl=54, ip_dscp=0xBE)
1004         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1005         self.verify_capture_labelled_ip6(
1006             self.pg0, rx, tx, [VppMplsLabel(32, ttl=53, exp=5)]
1007         )
1008
1009         #
1010         # add a recursive path, with output label, via the 1 label route
1011         #
1012         route_11_0_0_1 = VppIpRoute(
1013             self,
1014             "11.0.0.1",
1015             32,
1016             [VppRoutePath("10.0.0.1", 0xFFFFFFFF, labels=[VppMplsLabel(44)])],
1017         )
1018         route_11_0_0_1.add_vpp_config()
1019
1020         #
1021         # a stream that matches the route for 11.0.0.1, should pick up
1022         # the label stack for 11.0.0.1 and 10.0.0.1
1023         #
1024         tx = self.create_stream_ip4(self.pg0, "11.0.0.1")
1025         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1026         self.verify_capture_labelled_ip4(
1027             self.pg0, rx, tx, [VppMplsLabel(32), VppMplsLabel(44)]
1028         )
1029
1030         self.assertEqual(route_11_0_0_1.get_stats_to()["packets"], 257)
1031
1032         #
1033         # add a recursive path, with 2 labels, via the 3 label route
1034         #
1035         route_11_0_0_2 = VppIpRoute(
1036             self,
1037             "11.0.0.2",
1038             32,
1039             [
1040                 VppRoutePath(
1041                     "10.0.0.2", 0xFFFFFFFF, labels=[VppMplsLabel(44), VppMplsLabel(45)]
1042                 )
1043             ],
1044         )
1045         route_11_0_0_2.add_vpp_config()
1046
1047         #
1048         # a stream that matches the route for 11.0.0.1, should pick up
1049         # the label stack for 11.0.0.1 and 10.0.0.1
1050         #
1051         tx = self.create_stream_ip4(self.pg0, "11.0.0.2")
1052         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1053         self.verify_capture_labelled_ip4(
1054             self.pg0,
1055             rx,
1056             tx,
1057             [
1058                 VppMplsLabel(32),
1059                 VppMplsLabel(33),
1060                 VppMplsLabel(34),
1061                 VppMplsLabel(44),
1062                 VppMplsLabel(45),
1063             ],
1064         )
1065
1066         self.assertEqual(route_11_0_0_2.get_stats_to()["packets"], 257)
1067
1068         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1069         self.verify_capture_labelled_ip4(
1070             self.pg0,
1071             rx,
1072             tx,
1073             [
1074                 VppMplsLabel(32),
1075                 VppMplsLabel(33),
1076                 VppMplsLabel(34),
1077                 VppMplsLabel(44),
1078                 VppMplsLabel(45),
1079             ],
1080         )
1081
1082         self.assertEqual(route_11_0_0_2.get_stats_to()["packets"], 514)
1083
1084         #
1085         # cleanup
1086         #
1087         route_11_0_0_2.remove_vpp_config()
1088         route_11_0_0_1.remove_vpp_config()
1089         route_10_0_0_2.remove_vpp_config()
1090         route_10_0_0_1.remove_vpp_config()
1091
1092     def test_imposition_fragmentation(self):
1093         """MPLS label imposition fragmentation test"""
1094
1095         #
1096         # Add a ipv4 non-recursive route with a single out label
1097         #
1098         route_10_0_0_1 = VppIpRoute(
1099             self,
1100             "10.0.0.1",
1101             32,
1102             [
1103                 VppRoutePath(
1104                     self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[VppMplsLabel(32)]
1105                 )
1106             ],
1107         )
1108         route_10_0_0_1.add_vpp_config()
1109         route_1000_1 = VppIpRoute(
1110             self,
1111             "1000::1",
1112             128,
1113             [
1114                 VppRoutePath(
1115                     self.pg0.remote_ip6, self.pg0.sw_if_index, labels=[VppMplsLabel(32)]
1116                 )
1117             ],
1118         )
1119         route_1000_1.add_vpp_config()
1120
1121         #
1122         # a stream that matches the route for 10.0.0.1
1123         # PG0 is in the default table
1124         #
1125         tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
1126         for i in range(0, 257):
1127             self.extend_packet(tx[i], 10000)
1128
1129         #
1130         # 5 fragments per packet (257*5=1285)
1131         #
1132         rx = self.send_and_expect(self.pg0, tx, self.pg0, 1285)
1133         self.verify_capture_fragmented_labelled_ip4(
1134             self.pg0, rx, tx, [VppMplsLabel(32)]
1135         )
1136
1137         # packets with DF bit set generate ICMP
1138         for t in tx:
1139             t[IP].flags = "DF"
1140         rxs = self.send_and_expect_some(self.pg0, tx, self.pg0)
1141
1142         for rx in rxs:
1143             self.assertEqual(icmptypes[rx[ICMP].type], "dest-unreach")
1144             self.assertEqual(
1145                 icmpcodes[rx[ICMP].type][rx[ICMP].code], "fragmentation-needed"
1146             )
1147             # the link MTU is 9000, the MPLS over head is 4 bytes
1148             self.assertEqual(rx[ICMP].nexthopmtu, 9000 - 4)
1149
1150         self.assertEqual(
1151             self.statistics.get_err_counter(
1152                 "/err/mpls-frag/can't fragment this packet"
1153             ),
1154             len(tx),
1155         )
1156         #
1157         # a stream that matches the route for 1000::1/128
1158         # PG0 is in the default table
1159         #
1160         tx = self.create_stream_ip6(self.pg0, "1000::1")
1161         for i in range(0, 257):
1162             self.extend_packet(tx[i], 10000)
1163
1164         rxs = self.send_and_expect_some(self.pg0, tx, self.pg0)
1165         for rx in rxs:
1166             self.assertEqual(rx[ICMPv6PacketTooBig].mtu, 9000 - 4)
1167
1168         #
1169         # cleanup
1170         #
1171         route_10_0_0_1.remove_vpp_config()
1172
1173     def test_tunnel_pipe(self):
1174         """MPLS Tunnel Tests - Pipe"""
1175
1176         #
1177         # Create a tunnel with two out labels
1178         #
1179         mpls_tun = VppMPLSTunnelInterface(
1180             self,
1181             [
1182                 VppRoutePath(
1183                     self.pg0.remote_ip4,
1184                     self.pg0.sw_if_index,
1185                     labels=[VppMplsLabel(44), VppMplsLabel(46)],
1186                 )
1187             ],
1188         )
1189         mpls_tun.add_vpp_config()
1190         mpls_tun.admin_up()
1191
1192         #
1193         # add an unlabelled route through the new tunnel
1194         #
1195         route_10_0_0_3 = VppIpRoute(
1196             self, "10.0.0.3", 32, [VppRoutePath("0.0.0.0", mpls_tun._sw_if_index)]
1197         )
1198         route_10_0_0_3.add_vpp_config()
1199
1200         self.vapi.cli("clear trace")
1201         tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
1202         self.pg0.add_stream(tx)
1203
1204         self.pg_enable_capture(self.pg_interfaces)
1205         self.pg_start()
1206
1207         rx = self.pg0.get_capture()
1208         self.verify_capture_tunneled_ip4(
1209             self.pg0, rx, tx, [VppMplsLabel(44), VppMplsLabel(46)]
1210         )
1211
1212         #
1213         # add a labelled route through the new tunnel
1214         #
1215         route_10_0_0_4 = VppIpRoute(
1216             self,
1217             "10.0.0.4",
1218             32,
1219             [VppRoutePath("0.0.0.0", mpls_tun._sw_if_index, labels=[33])],
1220         )
1221         route_10_0_0_4.add_vpp_config()
1222
1223         self.vapi.cli("clear trace")
1224         tx = self.create_stream_ip4(self.pg0, "10.0.0.4")
1225         self.pg0.add_stream(tx)
1226
1227         self.pg_enable_capture(self.pg_interfaces)
1228         self.pg_start()
1229
1230         rx = self.pg0.get_capture()
1231         self.verify_capture_tunneled_ip4(
1232             self.pg0,
1233             rx,
1234             tx,
1235             [VppMplsLabel(44), VppMplsLabel(46), VppMplsLabel(33, ttl=255)],
1236         )
1237
1238         #
1239         # change tunnel's MTU to a low value
1240         #
1241         mpls_tun.set_l3_mtu(1200)
1242
1243         # send IP into the tunnel to be fragmented
1244         tx = self.create_stream_ip4(self.pg0, "10.0.0.3", payload_size=1500)
1245         rx = self.send_and_expect(self.pg0, tx, self.pg0, len(tx) * 2)
1246
1247         fake_tx = []
1248         for p in tx:
1249             fake_tx.append(p)
1250             fake_tx.append(p)
1251         self.verify_capture_tunneled_ip4(
1252             self.pg0, rx, fake_tx, [VppMplsLabel(44), VppMplsLabel(46)]
1253         )
1254
1255         # send MPLS into the tunnel to be fragmented
1256         tx = self.create_stream_ip4(self.pg0, "10.0.0.4", payload_size=1500)
1257         rx = self.send_and_expect(self.pg0, tx, self.pg0, len(tx) * 2)
1258
1259         fake_tx = []
1260         for p in tx:
1261             fake_tx.append(p)
1262             fake_tx.append(p)
1263         self.verify_capture_tunneled_ip4(
1264             self.pg0,
1265             rx,
1266             fake_tx,
1267             [VppMplsLabel(44), VppMplsLabel(46), VppMplsLabel(33, ttl=255)],
1268         )
1269
1270     def test_tunnel_uniform(self):
1271         """MPLS Tunnel Tests - Uniform"""
1272
1273         #
1274         # Create a tunnel with a single out label
1275         # The label stack is specified here from outer to inner
1276         #
1277         mpls_tun = VppMPLSTunnelInterface(
1278             self,
1279             [
1280                 VppRoutePath(
1281                     self.pg0.remote_ip4,
1282                     self.pg0.sw_if_index,
1283                     labels=[
1284                         VppMplsLabel(44, ttl=32),
1285                         VppMplsLabel(46, MplsLspMode.UNIFORM),
1286                     ],
1287                 )
1288             ],
1289         )
1290         mpls_tun.add_vpp_config()
1291         mpls_tun.admin_up()
1292
1293         #
1294         # add an unlabelled route through the new tunnel
1295         #
1296         route_10_0_0_3 = VppIpRoute(
1297             self, "10.0.0.3", 32, [VppRoutePath("0.0.0.0", mpls_tun._sw_if_index)]
1298         )
1299         route_10_0_0_3.add_vpp_config()
1300
1301         self.vapi.cli("clear trace")
1302         tx = self.create_stream_ip4(self.pg0, "10.0.0.3", ip_ttl=24)
1303         self.pg0.add_stream(tx)
1304
1305         self.pg_enable_capture(self.pg_interfaces)
1306         self.pg_start()
1307
1308         rx = self.pg0.get_capture()
1309         self.verify_capture_tunneled_ip4(
1310             self.pg0, rx, tx, [VppMplsLabel(44, ttl=32), VppMplsLabel(46, ttl=23)]
1311         )
1312
1313         #
1314         # add a labelled route through the new tunnel
1315         #
1316         route_10_0_0_4 = VppIpRoute(
1317             self,
1318             "10.0.0.4",
1319             32,
1320             [
1321                 VppRoutePath(
1322                     "0.0.0.0", mpls_tun._sw_if_index, labels=[VppMplsLabel(33, ttl=47)]
1323                 )
1324             ],
1325         )
1326         route_10_0_0_4.add_vpp_config()
1327
1328         self.vapi.cli("clear trace")
1329         tx = self.create_stream_ip4(self.pg0, "10.0.0.4")
1330         self.pg0.add_stream(tx)
1331
1332         self.pg_enable_capture(self.pg_interfaces)
1333         self.pg_start()
1334
1335         rx = self.pg0.get_capture()
1336         self.verify_capture_tunneled_ip4(
1337             self.pg0,
1338             rx,
1339             tx,
1340             [
1341                 VppMplsLabel(44, ttl=32),
1342                 VppMplsLabel(46, ttl=47),
1343                 VppMplsLabel(33, ttl=47),
1344             ],
1345         )
1346
1347     def test_mpls_tunnel_many(self):
1348         """MPLS Multiple Tunnels"""
1349
1350         for ii in range(100):
1351             mpls_tun = VppMPLSTunnelInterface(
1352                 self,
1353                 [
1354                     VppRoutePath(
1355                         self.pg0.remote_ip4,
1356                         self.pg0.sw_if_index,
1357                         labels=[
1358                             VppMplsLabel(44, ttl=32),
1359                             VppMplsLabel(46, MplsLspMode.UNIFORM),
1360                         ],
1361                     )
1362                 ],
1363             )
1364             mpls_tun.add_vpp_config()
1365             mpls_tun.admin_up()
1366         for ii in range(100):
1367             mpls_tun = VppMPLSTunnelInterface(
1368                 self,
1369                 [
1370                     VppRoutePath(
1371                         self.pg0.remote_ip4,
1372                         self.pg0.sw_if_index,
1373                         labels=[
1374                             VppMplsLabel(44, ttl=32),
1375                             VppMplsLabel(46, MplsLspMode.UNIFORM),
1376                         ],
1377                     )
1378                 ],
1379                 is_l2=1,
1380             )
1381             mpls_tun.add_vpp_config()
1382             mpls_tun.admin_up()
1383
1384     def test_v4_exp_null(self):
1385         """MPLS V4 Explicit NULL test"""
1386
1387         #
1388         # The first test case has an MPLS TTL of 0
1389         # all packet should be dropped
1390         #
1391         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(0, ttl=0)])
1392         self.send_and_assert_no_replies(self.pg0, tx, "MPLS TTL=0 packets forwarded")
1393
1394         #
1395         # a stream with a non-zero MPLS TTL
1396         # PG0 is in the default table
1397         #
1398         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(0)])
1399         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1400         self.verify_capture_ip4(self.pg0, rx, tx)
1401
1402         #
1403         # a stream with a non-zero MPLS TTL
1404         # PG1 is in table 1
1405         # we are ensuring the post-pop lookup occurs in the VRF table
1406         #
1407         tx = self.create_stream_labelled_ip4(self.pg1, [VppMplsLabel(0)])
1408         rx = self.send_and_expect(self.pg1, tx, self.pg1)
1409         self.verify_capture_ip4(self.pg1, rx, tx)
1410
1411     def test_v6_exp_null(self):
1412         """MPLS V6 Explicit NULL test"""
1413
1414         #
1415         # a stream with a non-zero MPLS TTL
1416         # PG0 is in the default table
1417         #
1418         tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(2)])
1419         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1420         self.verify_capture_ip6(self.pg0, rx, tx)
1421
1422         #
1423         # a stream with a non-zero MPLS TTL
1424         # PG1 is in table 1
1425         # we are ensuring the post-pop lookup occurs in the VRF table
1426         #
1427         tx = self.create_stream_labelled_ip6(self.pg1, [VppMplsLabel(2)])
1428         rx = self.send_and_expect(self.pg1, tx, self.pg1)
1429         self.verify_capture_ip6(self.pg0, rx, tx)
1430
1431     def test_deag(self):
1432         """MPLS Deagg"""
1433
1434         #
1435         # A de-agg route - next-hop lookup in default table
1436         #
1437         route_34_eos = VppMplsRoute(
1438             self, 34, 1, [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=0)]
1439         )
1440         route_34_eos.add_vpp_config()
1441
1442         #
1443         # ping an interface in the default table
1444         # PG0 is in the default table
1445         #
1446         tx = self.create_stream_labelled_ip4(
1447             self.pg0, [VppMplsLabel(34)], ping=1, ip_itf=self.pg0
1448         )
1449         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1450         self.verify_capture_ip4(self.pg0, rx, tx, ping_resp=1)
1451
1452         #
1453         # A de-agg route - next-hop lookup in non-default table
1454         #
1455         route_35_eos = VppMplsRoute(
1456             self, 35, 1, [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=1)]
1457         )
1458         route_35_eos.add_vpp_config()
1459         route_356_eos = VppMplsRoute(
1460             self,
1461             356,
1462             1,
1463             [VppRoutePath("0::0", 0xFFFFFFFF, nh_table_id=1)],
1464             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
1465         )
1466         route_356_eos.add_vpp_config()
1467
1468         #
1469         # ping an interface in the non-default table
1470         # PG0 is in the default table. packet arrive labelled in the
1471         # default table and egress unlabelled in the non-default
1472         #
1473         tx = self.create_stream_labelled_ip4(
1474             self.pg0, [VppMplsLabel(35)], ping=1, ip_itf=self.pg1
1475         )
1476         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1477         self.verify_capture_ip4(self.pg1, rx, tx, ping_resp=1)
1478         tx = self.create_stream_labelled_ip6(
1479             self.pg0, [VppMplsLabel(356)], ping=1, ip_itf=self.pg1
1480         )
1481         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1482         self.verify_capture_ip6(self.pg1, rx, tx, ping_resp=1)
1483
1484         #
1485         # Double pop
1486         #
1487         route_36_neos = VppMplsRoute(self, 36, 0, [VppRoutePath("0.0.0.0", 0xFFFFFFFF)])
1488         route_36_neos.add_vpp_config()
1489
1490         tx = self.create_stream_labelled_ip4(
1491             self.pg0, [VppMplsLabel(36), VppMplsLabel(35)], ping=1, ip_itf=self.pg1
1492         )
1493         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1494         self.verify_capture_ip4(self.pg1, rx, tx, ping_resp=1)
1495
1496         route_36_neos.remove_vpp_config()
1497         route_35_eos.remove_vpp_config()
1498         route_34_eos.remove_vpp_config()
1499
1500     def test_interface_rx(self):
1501         """MPLS Interface Receive"""
1502
1503         #
1504         # Add a non-recursive route that will forward the traffic
1505         # post-interface-rx
1506         #
1507         route_10_0_0_1 = VppIpRoute(
1508             self,
1509             "10.0.0.1",
1510             32,
1511             table_id=1,
1512             paths=[VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)],
1513         )
1514         route_10_0_0_1.add_vpp_config()
1515
1516         #
1517         # An interface receive label that maps traffic to RX on interface
1518         # pg1
1519         # by injecting the packet in on pg0, which is in table 0
1520         # doing an interface-rx on pg1 and matching a route in table 1
1521         # if the packet egresses, then we must have swapped to pg1
1522         # so as to have matched the route in table 1
1523         #
1524         route_34_eos = VppMplsRoute(
1525             self,
1526             34,
1527             1,
1528             [
1529                 VppRoutePath(
1530                     "0.0.0.0",
1531                     self.pg1.sw_if_index,
1532                     type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
1533                 )
1534             ],
1535         )
1536         route_34_eos.add_vpp_config()
1537
1538         #
1539         # ping an interface in the default table
1540         # PG0 is in the default table
1541         #
1542         tx = self.create_stream_labelled_ip4(
1543             self.pg0, [VppMplsLabel(34)], dst_ip="10.0.0.1"
1544         )
1545         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1546         self.verify_capture_ip4(self.pg1, rx, tx)
1547
1548     def test_mcast_mid_point(self):
1549         """MPLS Multicast Mid Point"""
1550
1551         #
1552         # Add a non-recursive route that will forward the traffic
1553         # post-interface-rx
1554         #
1555         route_10_0_0_1 = VppIpRoute(
1556             self,
1557             "10.0.0.1",
1558             32,
1559             table_id=1,
1560             paths=[VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)],
1561         )
1562         route_10_0_0_1.add_vpp_config()
1563
1564         #
1565         # Add a mcast entry that replicate to pg2 and pg3
1566         # and replicate to a interface-rx (like a bud node would)
1567         #
1568         route_3400_eos = VppMplsRoute(
1569             self,
1570             3400,
1571             1,
1572             [
1573                 VppRoutePath(
1574                     self.pg2.remote_ip4,
1575                     self.pg2.sw_if_index,
1576                     labels=[VppMplsLabel(3401)],
1577                 ),
1578                 VppRoutePath(
1579                     self.pg3.remote_ip4,
1580                     self.pg3.sw_if_index,
1581                     labels=[VppMplsLabel(3402)],
1582                 ),
1583                 VppRoutePath(
1584                     "0.0.0.0",
1585                     self.pg1.sw_if_index,
1586                     type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
1587                 ),
1588             ],
1589             is_multicast=1,
1590         )
1591         route_3400_eos.add_vpp_config()
1592
1593         #
1594         # ping an interface in the default table
1595         # PG0 is in the default table
1596         #
1597         self.vapi.cli("clear trace")
1598         tx = self.create_stream_labelled_ip4(
1599             self.pg0, [VppMplsLabel(3400, ttl=64)], n=257, dst_ip="10.0.0.1"
1600         )
1601         self.pg0.add_stream(tx)
1602
1603         self.pg_enable_capture(self.pg_interfaces)
1604         self.pg_start()
1605
1606         rx = self.pg1.get_capture(257)
1607         self.verify_capture_ip4(self.pg1, rx, tx)
1608
1609         rx = self.pg2.get_capture(257)
1610         self.verify_capture_labelled(self.pg2, rx, tx, [VppMplsLabel(3401, ttl=63)])
1611         rx = self.pg3.get_capture(257)
1612         self.verify_capture_labelled(self.pg3, rx, tx, [VppMplsLabel(3402, ttl=63)])
1613
1614     def test_mcast_head(self):
1615         """MPLS Multicast Head-end"""
1616
1617         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
1618         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
1619
1620         #
1621         # Create a multicast tunnel with two replications
1622         #
1623         mpls_tun = VppMPLSTunnelInterface(
1624             self,
1625             [
1626                 VppRoutePath(
1627                     self.pg2.remote_ip4, self.pg2.sw_if_index, labels=[VppMplsLabel(42)]
1628                 ),
1629                 VppRoutePath(
1630                     self.pg3.remote_ip4, self.pg3.sw_if_index, labels=[VppMplsLabel(43)]
1631                 ),
1632             ],
1633             is_multicast=1,
1634         )
1635         mpls_tun.add_vpp_config()
1636         mpls_tun.admin_up()
1637
1638         #
1639         # add an unlabelled route through the new tunnel
1640         #
1641         route_10_0_0_3 = VppIpRoute(
1642             self, "10.0.0.3", 32, [VppRoutePath("0.0.0.0", mpls_tun._sw_if_index)]
1643         )
1644         route_10_0_0_3.add_vpp_config()
1645
1646         self.vapi.cli("clear trace")
1647         tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
1648         self.pg0.add_stream(tx)
1649
1650         self.pg_enable_capture(self.pg_interfaces)
1651         self.pg_start()
1652
1653         rx = self.pg2.get_capture(257)
1654         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(42)])
1655         rx = self.pg3.get_capture(257)
1656         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(43)])
1657
1658         #
1659         # An an IP multicast route via the tunnel
1660         # A (*,G).
1661         # one accepting interface, pg0, 1 forwarding interface via the tunnel
1662         #
1663         route_232_1_1_1 = VppIpMRoute(
1664             self,
1665             "0.0.0.0",
1666             "232.1.1.1",
1667             32,
1668             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
1669             [
1670                 VppMRoutePath(
1671                     self.pg0.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT
1672                 ),
1673                 VppMRoutePath(
1674                     mpls_tun._sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD
1675                 ),
1676             ],
1677         )
1678         route_232_1_1_1.add_vpp_config()
1679         self.logger.info(self.vapi.cli("sh ip mfib index 0"))
1680
1681         self.vapi.cli("clear trace")
1682         tx = self.create_stream_ip4(self.pg0, "232.1.1.1")
1683         self.pg0.add_stream(tx)
1684
1685         self.pg_enable_capture(self.pg_interfaces)
1686         self.pg_start()
1687
1688         rx = self.pg2.get_capture(257)
1689         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(42)])
1690         rx = self.pg3.get_capture(257)
1691         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(43)])
1692
1693     def test_mcast_ip4_tail(self):
1694         """MPLS IPv4 Multicast Tail"""
1695
1696         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
1697         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
1698
1699         #
1700         # Add a multicast route that will forward the traffic
1701         # post-disposition
1702         #
1703         route_232_1_1_1 = VppIpMRoute(
1704             self,
1705             "0.0.0.0",
1706             "232.1.1.1",
1707             32,
1708             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
1709             table_id=1,
1710             paths=[
1711                 VppMRoutePath(
1712                     self.pg1.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD
1713                 )
1714             ],
1715         )
1716         route_232_1_1_1.add_vpp_config()
1717
1718         #
1719         # An interface receive label that maps traffic to RX on interface
1720         # pg1
1721         # by injecting the packet in on pg0, which is in table 0
1722         # doing an rpf-id  and matching a route in table 1
1723         # if the packet egresses, then we must have matched the route in
1724         # table 1
1725         #
1726         route_34_eos = VppMplsRoute(
1727             self,
1728             34,
1729             1,
1730             [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=1, rpf_id=55)],
1731             is_multicast=1,
1732             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
1733         )
1734
1735         route_34_eos.add_vpp_config()
1736
1737         #
1738         # Drop due to interface lookup miss
1739         #
1740         self.vapi.cli("clear trace")
1741         tx = self.create_stream_labelled_ip4(
1742             self.pg0, [VppMplsLabel(34)], dst_ip="232.1.1.1", n=1
1743         )
1744         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop none")
1745
1746         #
1747         # set the RPF-ID of the entry to match the input packet's
1748         #
1749         route_232_1_1_1.update_rpf_id(55)
1750         self.logger.info(self.vapi.cli("sh ip mfib index 1 232.1.1.1"))
1751
1752         tx = self.create_stream_labelled_ip4(
1753             self.pg0, [VppMplsLabel(34)], dst_ip="232.1.1.1"
1754         )
1755         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1756         self.verify_capture_ip4(self.pg1, rx, tx)
1757
1758         #
1759         # disposed packets have an invalid IPv4 checksum
1760         #
1761         tx = self.create_stream_labelled_ip4(
1762             self.pg0, [VppMplsLabel(34)], dst_ip="232.1.1.1", n=65, chksum=1
1763         )
1764         self.send_and_assert_no_replies(self.pg0, tx, "Invalid Checksum")
1765
1766         #
1767         # set the RPF-ID of the entry to not match the input packet's
1768         #
1769         route_232_1_1_1.update_rpf_id(56)
1770         tx = self.create_stream_labelled_ip4(
1771             self.pg0, [VppMplsLabel(34)], dst_ip="232.1.1.1"
1772         )
1773         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
1774
1775     def test_mcast_ip6_tail(self):
1776         """MPLS IPv6 Multicast Tail"""
1777
1778         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
1779         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
1780
1781         #
1782         # Add a multicast route that will forward the traffic
1783         # post-disposition
1784         #
1785         route_ff = VppIpMRoute(
1786             self,
1787             "::",
1788             "ff01::1",
1789             32,
1790             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
1791             table_id=1,
1792             paths=[
1793                 VppMRoutePath(
1794                     self.pg1.sw_if_index,
1795                     MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
1796                     proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
1797                 )
1798             ],
1799         )
1800         route_ff.add_vpp_config()
1801
1802         #
1803         # An interface receive label that maps traffic to RX on interface
1804         # pg1
1805         # by injecting the packet in on pg0, which is in table 0
1806         # doing an rpf-id  and matching a route in table 1
1807         # if the packet egresses, then we must have matched the route in
1808         # table 1
1809         #
1810         route_34_eos = VppMplsRoute(
1811             self,
1812             34,
1813             1,
1814             [VppRoutePath("::", 0xFFFFFFFF, nh_table_id=1, rpf_id=55)],
1815             is_multicast=1,
1816             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
1817         )
1818
1819         route_34_eos.add_vpp_config()
1820
1821         #
1822         # Drop due to interface lookup miss
1823         #
1824         tx = self.create_stream_labelled_ip6(
1825             self.pg0, [VppMplsLabel(34)], dst_ip="ff01::1"
1826         )
1827         self.send_and_assert_no_replies(self.pg0, tx, "RPF Miss")
1828
1829         #
1830         # set the RPF-ID of the entry to match the input packet's
1831         #
1832         route_ff.update_rpf_id(55)
1833
1834         tx = self.create_stream_labelled_ip6(
1835             self.pg0, [VppMplsLabel(34)], dst_ip="ff01::1"
1836         )
1837         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1838         self.verify_capture_ip6(self.pg1, rx, tx)
1839
1840         #
1841         # disposed packets have hop-limit = 1
1842         #
1843         tx = self.create_stream_labelled_ip6(
1844             self.pg0, [VppMplsLabel(34)], dst_ip="ff01::1", hlim=1
1845         )
1846         rx = self.send_and_expect_some(self.pg0, tx, self.pg0)
1847         self.verify_capture_ip6_icmp(self.pg0, rx, tx)
1848
1849         #
1850         # set the RPF-ID of the entry to not match the input packet's
1851         #
1852         route_ff.update_rpf_id(56)
1853         tx = self.create_stream_labelled_ip6(
1854             self.pg0, [VppMplsLabel(34)], dst_ip="ff01::1"
1855         )
1856         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
1857
1858     def test_6pe(self):
1859         """MPLS 6PE"""
1860
1861         #
1862         # Add a non-recursive route with a single out label
1863         #
1864         route_10_0_0_1 = VppIpRoute(
1865             self,
1866             "10.0.0.1",
1867             32,
1868             [
1869                 VppRoutePath(
1870                     self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[VppMplsLabel(45)]
1871                 )
1872             ],
1873         )
1874         route_10_0_0_1.add_vpp_config()
1875
1876         # bind a local label to the route
1877         binding = VppMplsIpBind(self, 44, "10.0.0.1", 32)
1878         binding.add_vpp_config()
1879
1880         #
1881         # a labelled v6 route that resolves through the v4
1882         #
1883         route_2001_3 = VppIpRoute(
1884             self,
1885             "2001::3",
1886             128,
1887             [VppRoutePath("10.0.0.1", INVALID_INDEX, labels=[VppMplsLabel(32)])],
1888         )
1889         route_2001_3.add_vpp_config()
1890
1891         tx = self.create_stream_ip6(self.pg0, "2001::3")
1892         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1893
1894         self.verify_capture_labelled_ip6(
1895             self.pg0, rx, tx, [VppMplsLabel(45), VppMplsLabel(32)]
1896         )
1897
1898         #
1899         # and a v4 recursive via the v6
1900         #
1901         route_20_3 = VppIpRoute(
1902             self,
1903             "20.0.0.3",
1904             32,
1905             [VppRoutePath("2001::3", INVALID_INDEX, labels=[VppMplsLabel(99)])],
1906         )
1907         route_20_3.add_vpp_config()
1908
1909         tx = self.create_stream_ip4(self.pg0, "20.0.0.3")
1910         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1911
1912         self.verify_capture_labelled_ip4(
1913             self.pg0, rx, tx, [VppMplsLabel(45), VppMplsLabel(32), VppMplsLabel(99)]
1914         )
1915
1916     def test_attached(self):
1917         """Attach Routes with Local Label"""
1918
1919         #
1920         # test that if a local label is associated with an attached/connected
1921         # prefix, that we can reach hosts in the prefix.
1922         #
1923         binding = VppMplsIpBind(
1924             self, 44, self.pg0._local_ip4_subnet, self.pg0.local_ip4_prefix_len
1925         )
1926         binding.add_vpp_config()
1927
1928         tx = (
1929             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
1930             / MPLS(label=44, ttl=64)
1931             / IP(src=self.pg0.remote_ip4, dst=self.pg0.remote_ip4)
1932             / UDP(sport=1234, dport=1234)
1933             / Raw(b"\xa5" * 100)
1934         )
1935         rxs = self.send_and_expect(self.pg0, [tx], self.pg0)
1936         for rx in rxs:
1937             # if there's an ARP then the label is linked to the glean
1938             # which is wrong.
1939             self.assertFalse(rx.haslayer(ARP))
1940             # it should be unicasted to the host
1941             self.assertEqual(rx[Ether].dst, self.pg0.remote_mac)
1942             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1943
1944
1945 class TestMPLSDisabled(VppTestCase):
1946     """MPLS disabled"""
1947
1948     @classmethod
1949     def setUpClass(cls):
1950         super(TestMPLSDisabled, cls).setUpClass()
1951
1952     @classmethod
1953     def tearDownClass(cls):
1954         super(TestMPLSDisabled, cls).tearDownClass()
1955
1956     def setUp(self):
1957         super(TestMPLSDisabled, self).setUp()
1958
1959         # create 2 pg interfaces
1960         self.create_pg_interfaces(range(2))
1961
1962         self.tbl = VppMplsTable(self, 0)
1963         self.tbl.add_vpp_config()
1964
1965         # PG0 is MPLS enabled
1966         self.pg0.admin_up()
1967         self.pg0.config_ip4()
1968         self.pg0.resolve_arp()
1969         self.pg0.enable_mpls()
1970
1971         # PG 1 is not MPLS enabled
1972         self.pg1.admin_up()
1973
1974     def tearDown(self):
1975         for i in self.pg_interfaces:
1976             i.unconfig_ip4()
1977             i.admin_down()
1978
1979         self.pg0.disable_mpls()
1980         super(TestMPLSDisabled, self).tearDown()
1981
1982     def test_mpls_disabled(self):
1983         """MPLS Disabled"""
1984
1985         self.logger.info(self.vapi.cli("show mpls interface"))
1986         self.logger.info(self.vapi.cli("show mpls interface pg1"))
1987         self.logger.info(self.vapi.cli("show mpls interface pg0"))
1988
1989         tx = (
1990             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
1991             / MPLS(label=32, ttl=64)
1992             / IPv6(src="2001::1", dst=self.pg0.remote_ip6)
1993             / UDP(sport=1234, dport=1234)
1994             / Raw(b"\xa5" * 100)
1995         )
1996
1997         #
1998         # A simple MPLS xconnect - eos label in label out
1999         #
2000         route_32_eos = VppMplsRoute(
2001             self,
2002             32,
2003             1,
2004             [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[33])],
2005         )
2006         route_32_eos.add_vpp_config()
2007
2008         #
2009         # PG1 does not forward IP traffic
2010         #
2011         self.send_and_assert_no_replies(self.pg1, tx, "MPLS disabled")
2012
2013         #
2014         # MPLS enable PG1
2015         #
2016         self.pg1.enable_mpls()
2017
2018         self.logger.info(self.vapi.cli("show mpls interface"))
2019         self.logger.info(self.vapi.cli("show mpls interface pg1"))
2020
2021         #
2022         # Now we get packets through
2023         #
2024         self.pg1.add_stream(tx)
2025         self.pg_enable_capture(self.pg_interfaces)
2026         self.pg_start()
2027
2028         rx = self.pg0.get_capture(1)
2029
2030         #
2031         # Disable PG1
2032         #
2033         self.pg1.disable_mpls()
2034
2035         #
2036         # PG1 does not forward IP traffic
2037         #
2038         self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
2039         self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
2040
2041
2042 class TestMPLSPIC(VppTestCase):
2043     """MPLS Prefix-Independent Convergence (PIC) edge convergence"""
2044
2045     @classmethod
2046     def setUpClass(cls):
2047         super(TestMPLSPIC, cls).setUpClass()
2048
2049     @classmethod
2050     def tearDownClass(cls):
2051         super(TestMPLSPIC, cls).tearDownClass()
2052
2053     def setUp(self):
2054         super(TestMPLSPIC, self).setUp()
2055
2056         # create 2 pg interfaces
2057         self.create_pg_interfaces(range(4))
2058
2059         mpls_tbl = VppMplsTable(self, 0)
2060         mpls_tbl.add_vpp_config()
2061         tbl4 = VppIpTable(self, 1)
2062         tbl4.add_vpp_config()
2063         tbl6 = VppIpTable(self, 1, is_ip6=1)
2064         tbl6.add_vpp_config()
2065
2066         # core links
2067         self.pg0.admin_up()
2068         self.pg0.config_ip4()
2069         self.pg0.resolve_arp()
2070         self.pg0.enable_mpls()
2071
2072         self.pg1.admin_up()
2073         self.pg1.config_ip4()
2074         self.pg1.resolve_arp()
2075         self.pg1.enable_mpls()
2076
2077         # VRF (customer facing) link
2078         self.pg2.admin_up()
2079         self.pg2.set_table_ip4(1)
2080         self.pg2.config_ip4()
2081         self.pg2.resolve_arp()
2082         self.pg2.set_table_ip6(1)
2083         self.pg2.config_ip6()
2084         self.pg2.resolve_ndp()
2085
2086         self.pg3.admin_up()
2087         self.pg3.set_table_ip4(1)
2088         self.pg3.config_ip4()
2089         self.pg3.resolve_arp()
2090         self.pg3.set_table_ip6(1)
2091         self.pg3.config_ip6()
2092         self.pg3.resolve_ndp()
2093
2094     def tearDown(self):
2095         self.pg0.disable_mpls()
2096         self.pg1.disable_mpls()
2097         for i in self.pg_interfaces:
2098             i.unconfig_ip4()
2099             i.unconfig_ip6()
2100             i.set_table_ip4(0)
2101             i.set_table_ip6(0)
2102             i.admin_down()
2103         super(TestMPLSPIC, self).tearDown()
2104
2105     def test_mpls_ibgp_pic(self):
2106         """MPLS iBGP Prefix-Independent Convergence (PIC) edge convergence
2107
2108         1) setup many iBGP VPN routes via a pair of iBGP peers.
2109         2) Check EMCP forwarding to these peers
2110         3) withdraw the IGP route to one of these peers.
2111         4) check forwarding continues to the remaining peer
2112         """
2113
2114         #
2115         # IGP+LDP core routes
2116         #
2117         core_10_0_0_45 = VppIpRoute(
2118             self,
2119             "10.0.0.45",
2120             32,
2121             [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[45])],
2122         )
2123         core_10_0_0_45.add_vpp_config()
2124
2125         core_10_0_0_46 = VppIpRoute(
2126             self,
2127             "10.0.0.46",
2128             32,
2129             [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index, labels=[46])],
2130         )
2131         core_10_0_0_46.add_vpp_config()
2132
2133         #
2134         # Lot's of VPN routes. We need more the 64 so VPP will build
2135         # the fast convergence indirection
2136         #
2137         vpn_routes = []
2138         pkts = []
2139         for ii in range(NUM_PKTS):
2140             dst = "192.168.1.%d" % ii
2141             vpn_routes.append(
2142                 VppIpRoute(
2143                     self,
2144                     dst,
2145                     32,
2146                     [
2147                         VppRoutePath(
2148                             "10.0.0.45",
2149                             0xFFFFFFFF,
2150                             labels=[145],
2151                             flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_HOST,
2152                         ),
2153                         VppRoutePath(
2154                             "10.0.0.46",
2155                             0xFFFFFFFF,
2156                             labels=[146],
2157                             flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_HOST,
2158                         ),
2159                     ],
2160                     table_id=1,
2161                 )
2162             )
2163             vpn_routes[ii].add_vpp_config()
2164
2165             pkts.append(
2166                 Ether(dst=self.pg2.local_mac, src=self.pg2.remote_mac)
2167                 / IP(src=self.pg2.remote_ip4, dst=dst)
2168                 / UDP(sport=1234, dport=1234)
2169                 / Raw(b"\xa5" * 100)
2170             )
2171
2172         #
2173         # Send the packet stream (one pkt to each VPN route)
2174         #  - expect a 50-50 split of the traffic
2175         #
2176         self.pg2.add_stream(pkts)
2177         self.pg_enable_capture(self.pg_interfaces)
2178         self.pg_start()
2179
2180         rx0 = self.pg0._get_capture(NUM_PKTS)
2181         rx1 = self.pg1._get_capture(NUM_PKTS)
2182
2183         # not testing the LB hashing algorithm so we're not concerned
2184         # with the split ratio, just as long as neither is 0
2185         self.assertNotEqual(0, len(rx0))
2186         self.assertNotEqual(0, len(rx1))
2187         self.assertEqual(
2188             len(pkts),
2189             len(rx0) + len(rx1),
2190             "Expected all (%s) packets across both ECMP paths. "
2191             "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)),
2192         )
2193
2194         #
2195         # use a test CLI command to stop the FIB walk process, this
2196         # will prevent the FIB converging the VPN routes and thus allow
2197         # us to probe the interim (post-fail, pre-converge) state
2198         #
2199         self.vapi.ppcli("test fib-walk-process disable")
2200
2201         #
2202         # Withdraw one of the IGP routes
2203         #
2204         core_10_0_0_46.remove_vpp_config()
2205
2206         #
2207         # now all packets should be forwarded through the remaining peer
2208         #
2209         self.vapi.ppcli("clear trace")
2210         self.pg2.add_stream(pkts)
2211         self.pg_enable_capture(self.pg_interfaces)
2212         self.pg_start()
2213
2214         rx0 = self.pg0.get_capture(NUM_PKTS)
2215         self.assertEqual(
2216             len(pkts),
2217             len(rx0),
2218             "Expected all (%s) packets across single path. "
2219             "rx0: %s." % (len(pkts), len(rx0)),
2220         )
2221
2222         #
2223         # enable the FIB walk process to converge the FIB
2224         #
2225         self.vapi.ppcli("test fib-walk-process enable")
2226
2227         #
2228         # packets should still be forwarded through the remaining peer
2229         #
2230         self.pg2.add_stream(pkts)
2231         self.pg_enable_capture(self.pg_interfaces)
2232         self.pg_start()
2233
2234         rx0 = self.pg0.get_capture(NUM_PKTS)
2235         self.assertEqual(
2236             len(pkts),
2237             len(rx0),
2238             "Expected all (%s) packets across single path. "
2239             "rx0: %s." % (len(pkts), len(rx0)),
2240         )
2241
2242         #
2243         # Add the IGP route back and we return to load-balancing
2244         #
2245         core_10_0_0_46.add_vpp_config()
2246
2247         self.pg2.add_stream(pkts)
2248         self.pg_enable_capture(self.pg_interfaces)
2249         self.pg_start()
2250
2251         rx0 = self.pg0._get_capture(NUM_PKTS)
2252         rx1 = self.pg1._get_capture(NUM_PKTS)
2253         self.assertNotEqual(0, len(rx0))
2254         self.assertNotEqual(0, len(rx1))
2255         self.assertEqual(
2256             len(pkts),
2257             len(rx0) + len(rx1),
2258             "Expected all (%s) packets across both ECMP paths. "
2259             "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)),
2260         )
2261
2262     def test_mpls_ebgp_pic(self):
2263         """MPLS eBGP Prefix-Independent Convergence (PIC) edge convergence
2264
2265         1) setup many eBGP VPN routes via a pair of eBGP peers.
2266         2) Check EMCP forwarding to these peers
2267         3) withdraw one eBGP path - expect LB across remaining eBGP
2268         """
2269
2270         #
2271         # Lot's of VPN routes. We need more the 64 so VPP will build
2272         # the fast convergence indirection
2273         #
2274         vpn_routes = []
2275         vpn_bindings = []
2276         pkts = []
2277         for ii in range(NUM_PKTS):
2278             dst = "192.168.1.%d" % ii
2279             local_label = 1600 + ii
2280             vpn_routes.append(
2281                 VppIpRoute(
2282                     self,
2283                     dst,
2284                     32,
2285                     [
2286                         VppRoutePath(
2287                             self.pg2.remote_ip4,
2288                             0xFFFFFFFF,
2289                             nh_table_id=1,
2290                             flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED,
2291                         ),
2292                         VppRoutePath(
2293                             self.pg3.remote_ip4,
2294                             0xFFFFFFFF,
2295                             nh_table_id=1,
2296                             flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED,
2297                         ),
2298                     ],
2299                     table_id=1,
2300                 )
2301             )
2302             vpn_routes[ii].add_vpp_config()
2303
2304             vpn_bindings.append(
2305                 VppMplsIpBind(self, local_label, dst, 32, ip_table_id=1)
2306             )
2307             vpn_bindings[ii].add_vpp_config()
2308
2309             pkts.append(
2310                 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2311                 / MPLS(label=local_label, ttl=64)
2312                 / IP(src=self.pg0.remote_ip4, dst=dst)
2313                 / UDP(sport=1234, dport=1234)
2314                 / Raw(b"\xa5" * 100)
2315             )
2316
2317         #
2318         # Send the packet stream (one pkt to each VPN route)
2319         #  - expect a 50-50 split of the traffic
2320         #
2321         self.pg0.add_stream(pkts)
2322         self.pg_enable_capture(self.pg_interfaces)
2323         self.pg_start()
2324
2325         rx0 = self.pg2._get_capture(NUM_PKTS)
2326         rx1 = self.pg3._get_capture(NUM_PKTS)
2327
2328         # not testing the LB hashing algorithm so we're not concerned
2329         # with the split ratio, just as long as neither is 0
2330         self.assertNotEqual(0, len(rx0))
2331         self.assertNotEqual(0, len(rx1))
2332         self.assertEqual(
2333             len(pkts),
2334             len(rx0) + len(rx1),
2335             "Expected all (%s) packets across both ECMP paths. "
2336             "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)),
2337         )
2338
2339         #
2340         # use a test CLI command to stop the FIB walk process, this
2341         # will prevent the FIB converging the VPN routes and thus allow
2342         # us to probe the interim (post-fail, pre-converge) state
2343         #
2344         self.vapi.ppcli("test fib-walk-process disable")
2345
2346         #
2347         # withdraw the connected prefix on the interface.
2348         #
2349         self.pg2.unconfig_ip4()
2350
2351         #
2352         # now all packets should be forwarded through the remaining peer
2353         #
2354         self.pg0.add_stream(pkts)
2355         self.pg_enable_capture(self.pg_interfaces)
2356         self.pg_start()
2357
2358         rx0 = self.pg3.get_capture(NUM_PKTS)
2359         self.assertEqual(
2360             len(pkts),
2361             len(rx0),
2362             "Expected all (%s) packets across single path. "
2363             "rx0: %s." % (len(pkts), len(rx0)),
2364         )
2365
2366         #
2367         # enable the FIB walk process to converge the FIB
2368         #
2369         self.vapi.ppcli("test fib-walk-process enable")
2370
2371         #
2372         # packets should still be forwarded through the remaining peer
2373         #
2374         self.pg0.add_stream(pkts)
2375         self.pg_enable_capture(self.pg_interfaces)
2376         self.pg_start()
2377
2378         rx0 = self.pg3.get_capture(NUM_PKTS)
2379         self.assertEqual(
2380             len(pkts),
2381             len(rx0),
2382             "Expected all (%s) packets across single path. "
2383             "rx0: %s." % (len(pkts), len(rx0)),
2384         )
2385
2386         #
2387         # put the connected routes back
2388         #
2389         self.pg2.config_ip4()
2390         self.pg2.resolve_arp()
2391
2392         self.pg0.add_stream(pkts)
2393         self.pg_enable_capture(self.pg_interfaces)
2394         self.pg_start()
2395
2396         rx0 = self.pg2._get_capture(NUM_PKTS)
2397         rx1 = self.pg3._get_capture(NUM_PKTS)
2398         self.assertNotEqual(0, len(rx0))
2399         self.assertNotEqual(0, len(rx1))
2400         self.assertEqual(
2401             len(pkts),
2402             len(rx0) + len(rx1),
2403             "Expected all (%s) packets across both ECMP paths. "
2404             "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)),
2405         )
2406
2407     def test_mpls_v6_ebgp_pic(self):
2408         """MPLSv6 eBGP Prefix-Independent Convergence (PIC) edge convergence
2409
2410         1) setup many eBGP VPNv6 routes via a pair of eBGP peers
2411         2) Check EMCP forwarding to these peers
2412         3) withdraw one eBGP path - expect LB across remaining eBGP
2413         """
2414
2415         #
2416         # Lot's of VPN routes. We need more the 64 so VPP will build
2417         # the fast convergence indirection
2418         #
2419         vpn_routes = []
2420         vpn_bindings = []
2421         pkts = []
2422         for ii in range(NUM_PKTS):
2423             dst = "3000::%d" % ii
2424             local_label = 1600 + ii
2425             vpn_routes.append(
2426                 VppIpRoute(
2427                     self,
2428                     dst,
2429                     128,
2430                     [
2431                         VppRoutePath(
2432                             self.pg2.remote_ip6,
2433                             0xFFFFFFFF,
2434                             nh_table_id=1,
2435                             flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED,
2436                         ),
2437                         VppRoutePath(
2438                             self.pg3.remote_ip6,
2439                             0xFFFFFFFF,
2440                             nh_table_id=1,
2441                             flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED,
2442                         ),
2443                     ],
2444                     table_id=1,
2445                 )
2446             )
2447             vpn_routes[ii].add_vpp_config()
2448
2449             vpn_bindings.append(
2450                 VppMplsIpBind(self, local_label, dst, 128, ip_table_id=1)
2451             )
2452             vpn_bindings[ii].add_vpp_config()
2453
2454             pkts.append(
2455                 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2456                 / MPLS(label=local_label, ttl=64)
2457                 / IPv6(src=self.pg0.remote_ip6, dst=dst)
2458                 / UDP(sport=1234, dport=1234)
2459                 / Raw(b"\xa5" * 100)
2460             )
2461             self.logger.info(self.vapi.cli("sh ip6 fib %s" % dst))
2462
2463         self.pg0.add_stream(pkts)
2464         self.pg_enable_capture(self.pg_interfaces)
2465         self.pg_start()
2466
2467         rx0 = self.pg2._get_capture(NUM_PKTS)
2468         rx1 = self.pg3._get_capture(NUM_PKTS)
2469         self.assertNotEqual(0, len(rx0))
2470         self.assertNotEqual(0, len(rx1))
2471         self.assertEqual(
2472             len(pkts),
2473             len(rx0) + len(rx1),
2474             "Expected all (%s) packets across both ECMP paths. "
2475             "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)),
2476         )
2477
2478         #
2479         # use a test CLI command to stop the FIB walk process, this
2480         # will prevent the FIB converging the VPN routes and thus allow
2481         # us to probe the interim (post-fail, pre-converge) state
2482         #
2483         self.vapi.ppcli("test fib-walk-process disable")
2484
2485         #
2486         # withdraw the connected prefix on the interface.
2487         # and shutdown the interface so the ND cache is flushed.
2488         #
2489         self.pg2.unconfig_ip6()
2490         self.pg2.admin_down()
2491
2492         #
2493         # now all packets should be forwarded through the remaining peer
2494         #
2495         self.pg0.add_stream(pkts)
2496         self.pg_enable_capture(self.pg_interfaces)
2497         self.pg_start()
2498
2499         rx0 = self.pg3.get_capture(NUM_PKTS)
2500         self.assertEqual(
2501             len(pkts),
2502             len(rx0),
2503             "Expected all (%s) packets across single path. "
2504             "rx0: %s." % (len(pkts), len(rx0)),
2505         )
2506
2507         #
2508         # enable the FIB walk process to converge the FIB
2509         #
2510         self.vapi.ppcli("test fib-walk-process enable")
2511         self.pg0.add_stream(pkts)
2512         self.pg_enable_capture(self.pg_interfaces)
2513         self.pg_start()
2514
2515         rx0 = self.pg3.get_capture(NUM_PKTS)
2516         self.assertEqual(
2517             len(pkts),
2518             len(rx0),
2519             "Expected all (%s) packets across single path. "
2520             "rx0: %s." % (len(pkts), len(rx0)),
2521         )
2522
2523         #
2524         # put the connected routes back
2525         #
2526         self.logger.info(self.vapi.cli("sh log"))
2527         self.pg2.admin_up()
2528         self.pg2.config_ip6()
2529         self.pg2.resolve_ndp()
2530
2531         self.pg0.add_stream(pkts)
2532         self.pg_enable_capture(self.pg_interfaces)
2533         self.pg_start()
2534
2535         rx0 = self.pg2._get_capture(NUM_PKTS)
2536         rx1 = self.pg3._get_capture(NUM_PKTS)
2537         self.assertNotEqual(0, len(rx0))
2538         self.assertNotEqual(0, len(rx1))
2539         self.assertEqual(
2540             len(pkts),
2541             len(rx0) + len(rx1),
2542             "Expected all (%s) packets across both ECMP paths. "
2543             "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)),
2544         )
2545
2546
2547 class TestMPLSL2(VppTestCase):
2548     """MPLS-L2"""
2549
2550     @classmethod
2551     def setUpClass(cls):
2552         super(TestMPLSL2, cls).setUpClass()
2553
2554     @classmethod
2555     def tearDownClass(cls):
2556         super(TestMPLSL2, cls).tearDownClass()
2557
2558     def setUp(self):
2559         super(TestMPLSL2, self).setUp()
2560
2561         # create 2 pg interfaces
2562         self.create_pg_interfaces(range(2))
2563
2564         # create the default MPLS table
2565         self.tables = []
2566         tbl = VppMplsTable(self, 0)
2567         tbl.add_vpp_config()
2568         self.tables.append(tbl)
2569
2570         # use pg0 as the core facing interface, don't resolve ARP
2571         self.pg0.admin_up()
2572         self.pg0.config_ip4()
2573         self.pg0.enable_mpls()
2574
2575         # use the other 2 for customer facing L2 links
2576         for i in self.pg_interfaces[1:]:
2577             i.admin_up()
2578
2579     def tearDown(self):
2580         for i in self.pg_interfaces[1:]:
2581             i.admin_down()
2582
2583         self.pg0.disable_mpls()
2584         self.pg0.unconfig_ip4()
2585         self.pg0.admin_down()
2586         super(TestMPLSL2, self).tearDown()
2587
2588     def verify_capture_tunneled_ethernet(self, capture, sent, mpls_labels):
2589         capture = verify_filter(capture, sent)
2590
2591         self.assertEqual(len(capture), len(sent))
2592
2593         for i in range(len(capture)):
2594             tx = sent[i]
2595             rx = capture[i]
2596
2597             # the MPLS TTL is 255 since it enters a new tunnel
2598             verify_mpls_stack(self, rx, mpls_labels)
2599
2600             tx_eth = tx[Ether]
2601             rx_eth = Ether(scapy.compat.raw(rx[MPLS].payload))
2602
2603             self.assertEqual(rx_eth.src, tx_eth.src)
2604             self.assertEqual(rx_eth.dst, tx_eth.dst)
2605
2606     def verify_arp_req(self, rx, smac, sip, dip):
2607         ether = rx[Ether]
2608         self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
2609         self.assertEqual(ether.src, smac)
2610
2611         arp = rx[ARP]
2612         self.assertEqual(arp.hwtype, 1)
2613         self.assertEqual(arp.ptype, 0x800)
2614         self.assertEqual(arp.hwlen, 6)
2615         self.assertEqual(arp.plen, 4)
2616         self.assertEqual(arp.op, ARP.who_has)
2617         self.assertEqual(arp.hwsrc, smac)
2618         self.assertEqual(arp.hwdst, "00:00:00:00:00:00")
2619         self.assertEqual(arp.psrc, sip)
2620         self.assertEqual(arp.pdst, dip)
2621
2622     def test_vpws(self):
2623         """Virtual Private Wire Service"""
2624
2625         #
2626         # Create an MPLS tunnel that pushes 1 label
2627         # For Ethernet over MPLS the uniform mode is irrelevant since ttl/cos
2628         # information is not in the packet, but we test it works anyway
2629         #
2630         mpls_tun_1 = VppMPLSTunnelInterface(
2631             self,
2632             [
2633                 VppRoutePath(
2634                     self.pg0.remote_ip4,
2635                     self.pg0.sw_if_index,
2636                     labels=[VppMplsLabel(42, MplsLspMode.UNIFORM)],
2637                 )
2638             ],
2639             is_l2=1,
2640         )
2641         mpls_tun_1.add_vpp_config()
2642         mpls_tun_1.admin_up()
2643
2644         #
2645         # Create a label entry to for 55 that does L2 input to the tunnel
2646         #
2647         route_55_eos = VppMplsRoute(
2648             self,
2649             55,
2650             1,
2651             [
2652                 VppRoutePath(
2653                     "0.0.0.0",
2654                     mpls_tun_1.sw_if_index,
2655                     type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2656                     proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET,
2657                 )
2658             ],
2659             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET,
2660         )
2661         route_55_eos.add_vpp_config()
2662
2663         #
2664         # Cross-connect the tunnel with one of the customers L2 interfaces
2665         #
2666         self.vapi.sw_interface_set_l2_xconnect(
2667             self.pg1.sw_if_index, mpls_tun_1.sw_if_index, enable=1
2668         )
2669         self.vapi.sw_interface_set_l2_xconnect(
2670             mpls_tun_1.sw_if_index, self.pg1.sw_if_index, enable=1
2671         )
2672
2673         #
2674         # inject a packet from the core
2675         #
2676         pcore = (
2677             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2678             / MPLS(label=55, ttl=64)
2679             / Ether(dst="00:00:de:ad:ba:be", src="00:00:de:ad:be:ef")
2680             / IP(src="10.10.10.10", dst="11.11.11.11")
2681             / UDP(sport=1234, dport=1234)
2682             / Raw(b"\xa5" * 100)
2683         )
2684
2685         tx0 = pcore * NUM_PKTS
2686         rx0 = self.send_and_expect(self.pg0, tx0, self.pg1)
2687         payload = pcore[MPLS].payload
2688
2689         self.assertEqual(rx0[0][Ether].dst, payload[Ether].dst)
2690         self.assertEqual(rx0[0][Ether].src, payload[Ether].src)
2691
2692         #
2693         # Inject a packet from the customer/L2 side
2694         # there's no resolved ARP entry so the first packet we see should be
2695         # an ARP request
2696         #
2697         tx1 = pcore[MPLS].payload
2698         rx1 = self.send_and_expect(self.pg1, [tx1], self.pg0)
2699
2700         self.verify_arp_req(
2701             rx1[0], self.pg0.local_mac, self.pg0.local_ip4, self.pg0.remote_ip4
2702         )
2703
2704         #
2705         # resolve the ARP entries and send again
2706         #
2707         self.pg0.resolve_arp()
2708         tx1 = pcore[MPLS].payload * NUM_PKTS
2709         rx1 = self.send_and_expect(self.pg1, tx1, self.pg0)
2710
2711         self.verify_capture_tunneled_ethernet(rx1, tx1, [VppMplsLabel(42)])
2712
2713     def test_vpls(self):
2714         """Virtual Private LAN Service"""
2715
2716         # we skipped this in the setup
2717         self.pg0.resolve_arp()
2718
2719         #
2720         # Create a L2 MPLS tunnels
2721         #
2722         mpls_tun1 = VppMPLSTunnelInterface(
2723             self,
2724             [
2725                 VppRoutePath(
2726                     self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[VppMplsLabel(42)]
2727                 )
2728             ],
2729             is_l2=1,
2730         )
2731         mpls_tun1.add_vpp_config()
2732         mpls_tun1.admin_up()
2733
2734         mpls_tun2 = VppMPLSTunnelInterface(
2735             self,
2736             [
2737                 VppRoutePath(
2738                     self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[VppMplsLabel(43)]
2739                 )
2740             ],
2741             is_l2=1,
2742         )
2743         mpls_tun2.add_vpp_config()
2744         mpls_tun2.admin_up()
2745
2746         #
2747         # Create a label entries, 55 and 56, that do L2 input to the tunnel
2748         # the latter includes a Psuedo Wire Control Word
2749         #
2750         route_55_eos = VppMplsRoute(
2751             self,
2752             55,
2753             1,
2754             [
2755                 VppRoutePath(
2756                     "0.0.0.0",
2757                     mpls_tun1.sw_if_index,
2758                     type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2759                     proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET,
2760                 )
2761             ],
2762             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET,
2763         )
2764
2765         route_56_eos = VppMplsRoute(
2766             self,
2767             56,
2768             1,
2769             [
2770                 VppRoutePath(
2771                     "0.0.0.0",
2772                     mpls_tun2.sw_if_index,
2773                     type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2774                     flags=FibPathFlags.FIB_PATH_FLAG_POP_PW_CW,
2775                     proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET,
2776                 )
2777             ],
2778             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET,
2779         )
2780
2781         # move me
2782         route_56_eos.add_vpp_config()
2783         route_55_eos.add_vpp_config()
2784
2785         self.logger.info(self.vapi.cli("sh mpls fib 56"))
2786
2787         #
2788         # add to tunnel to the customers bridge-domain
2789         #
2790         self.vapi.sw_interface_set_l2_bridge(
2791             rx_sw_if_index=mpls_tun1.sw_if_index, bd_id=1
2792         )
2793         self.vapi.sw_interface_set_l2_bridge(
2794             rx_sw_if_index=mpls_tun2.sw_if_index, bd_id=1
2795         )
2796         self.vapi.sw_interface_set_l2_bridge(
2797             rx_sw_if_index=self.pg1.sw_if_index, bd_id=1
2798         )
2799
2800         #
2801         # Packet from host on the customer interface to each host
2802         # reachable over the core, and vice-versa
2803         #
2804         p_cust1 = (
2805             Ether(dst="00:00:de:ad:ba:b1", src="00:00:de:ad:be:ef")
2806             / IP(src="10.10.10.10", dst="11.11.11.11")
2807             / UDP(sport=1234, dport=1234)
2808             / Raw(b"\xa5" * 100)
2809         )
2810         p_cust2 = (
2811             Ether(dst="00:00:de:ad:ba:b2", src="00:00:de:ad:be:ef")
2812             / IP(src="10.10.10.10", dst="11.11.11.12")
2813             / UDP(sport=1234, dport=1234)
2814             / Raw(b"\xa5" * 100)
2815         )
2816         p_core1 = (
2817             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2818             / MPLS(label=55, ttl=64)
2819             / Ether(src="00:00:de:ad:ba:b1", dst="00:00:de:ad:be:ef")
2820             / IP(dst="10.10.10.10", src="11.11.11.11")
2821             / UDP(sport=1234, dport=1234)
2822             / Raw(b"\xa5" * 100)
2823         )
2824         p_core2 = (
2825             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2826             / MPLS(label=56, ttl=64)
2827             / Raw(b"\x01" * 4)
2828             / Ether(src="00:00:de:ad:ba:b2", dst="00:00:de:ad:be:ef")  # PW CW
2829             / IP(dst="10.10.10.10", src="11.11.11.12")
2830             / UDP(sport=1234, dport=1234)
2831             / Raw(b"\xa5" * 100)
2832         )
2833
2834         #
2835         # The BD is learning, so send in one of each packet to learn
2836         #
2837
2838         # 2 packets due to BD flooding
2839         rx = self.send_and_expect(self.pg1, p_cust1, self.pg0, n_rx=2)
2840         rx = self.send_and_expect(self.pg1, p_cust2, self.pg0, n_rx=2)
2841
2842         # we've learnt this so expect it be be forwarded not flooded
2843         rx = self.send_and_expect(self.pg0, [p_core1], self.pg1)
2844         self.assertEqual(rx[0][Ether].dst, p_cust1[Ether].src)
2845         self.assertEqual(rx[0][Ether].src, p_cust1[Ether].dst)
2846
2847         rx = self.send_and_expect(self.pg0, [p_core2], self.pg1)
2848         self.assertEqual(rx[0][Ether].dst, p_cust2[Ether].src)
2849         self.assertEqual(rx[0][Ether].src, p_cust2[Ether].dst)
2850
2851         #
2852         # now a stream in each direction from each host
2853         #
2854         rx = self.send_and_expect(self.pg1, p_cust1 * NUM_PKTS, self.pg0)
2855         self.verify_capture_tunneled_ethernet(
2856             rx, p_cust1 * NUM_PKTS, [VppMplsLabel(42)]
2857         )
2858
2859         rx = self.send_and_expect(self.pg1, p_cust2 * NUM_PKTS, self.pg0)
2860         self.verify_capture_tunneled_ethernet(
2861             rx, p_cust2 * NUM_PKTS, [VppMplsLabel(43)]
2862         )
2863
2864         rx = self.send_and_expect(self.pg0, p_core1 * NUM_PKTS, self.pg1)
2865         rx = self.send_and_expect(self.pg0, p_core2 * NUM_PKTS, self.pg1)
2866
2867         #
2868         # remove interfaces from customers bridge-domain
2869         #
2870         self.vapi.sw_interface_set_l2_bridge(
2871             rx_sw_if_index=mpls_tun1.sw_if_index, bd_id=1, enable=0
2872         )
2873         self.vapi.sw_interface_set_l2_bridge(
2874             rx_sw_if_index=mpls_tun2.sw_if_index, bd_id=1, enable=0
2875         )
2876         self.vapi.sw_interface_set_l2_bridge(
2877             rx_sw_if_index=self.pg1.sw_if_index, bd_id=1, enable=0
2878         )
2879
2880
2881 if __name__ == "__main__":
2882     unittest.main(testRunner=VppTestRunner)