ip: Use .api declared error counters
[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("/err/mpls-frag/dont_fragment_set"),
1152             len(tx),
1153         )
1154         #
1155         # a stream that matches the route for 1000::1/128
1156         # PG0 is in the default table
1157         #
1158         tx = self.create_stream_ip6(self.pg0, "1000::1")
1159         for i in range(0, 257):
1160             self.extend_packet(tx[i], 10000)
1161
1162         rxs = self.send_and_expect_some(self.pg0, tx, self.pg0)
1163         for rx in rxs:
1164             self.assertEqual(rx[ICMPv6PacketTooBig].mtu, 9000 - 4)
1165
1166         #
1167         # cleanup
1168         #
1169         route_10_0_0_1.remove_vpp_config()
1170
1171     def test_tunnel_pipe(self):
1172         """MPLS Tunnel Tests - Pipe"""
1173
1174         #
1175         # Create a tunnel with two out labels
1176         #
1177         mpls_tun = VppMPLSTunnelInterface(
1178             self,
1179             [
1180                 VppRoutePath(
1181                     self.pg0.remote_ip4,
1182                     self.pg0.sw_if_index,
1183                     labels=[VppMplsLabel(44), VppMplsLabel(46)],
1184                 )
1185             ],
1186         )
1187         mpls_tun.add_vpp_config()
1188         mpls_tun.admin_up()
1189
1190         #
1191         # add an unlabelled route through the new tunnel
1192         #
1193         route_10_0_0_3 = VppIpRoute(
1194             self, "10.0.0.3", 32, [VppRoutePath("0.0.0.0", mpls_tun._sw_if_index)]
1195         )
1196         route_10_0_0_3.add_vpp_config()
1197
1198         self.vapi.cli("clear trace")
1199         tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
1200         self.pg0.add_stream(tx)
1201
1202         self.pg_enable_capture(self.pg_interfaces)
1203         self.pg_start()
1204
1205         rx = self.pg0.get_capture()
1206         self.verify_capture_tunneled_ip4(
1207             self.pg0, rx, tx, [VppMplsLabel(44), VppMplsLabel(46)]
1208         )
1209
1210         #
1211         # add a labelled route through the new tunnel
1212         #
1213         route_10_0_0_4 = VppIpRoute(
1214             self,
1215             "10.0.0.4",
1216             32,
1217             [VppRoutePath("0.0.0.0", mpls_tun._sw_if_index, labels=[33])],
1218         )
1219         route_10_0_0_4.add_vpp_config()
1220
1221         self.vapi.cli("clear trace")
1222         tx = self.create_stream_ip4(self.pg0, "10.0.0.4")
1223         self.pg0.add_stream(tx)
1224
1225         self.pg_enable_capture(self.pg_interfaces)
1226         self.pg_start()
1227
1228         rx = self.pg0.get_capture()
1229         self.verify_capture_tunneled_ip4(
1230             self.pg0,
1231             rx,
1232             tx,
1233             [VppMplsLabel(44), VppMplsLabel(46), VppMplsLabel(33, ttl=255)],
1234         )
1235
1236         #
1237         # change tunnel's MTU to a low value
1238         #
1239         mpls_tun.set_l3_mtu(1200)
1240
1241         # send IP into the tunnel to be fragmented
1242         tx = self.create_stream_ip4(self.pg0, "10.0.0.3", payload_size=1500)
1243         rx = self.send_and_expect(self.pg0, tx, self.pg0, len(tx) * 2)
1244
1245         fake_tx = []
1246         for p in tx:
1247             fake_tx.append(p)
1248             fake_tx.append(p)
1249         self.verify_capture_tunneled_ip4(
1250             self.pg0, rx, fake_tx, [VppMplsLabel(44), VppMplsLabel(46)]
1251         )
1252
1253         # send MPLS into the tunnel to be fragmented
1254         tx = self.create_stream_ip4(self.pg0, "10.0.0.4", payload_size=1500)
1255         rx = self.send_and_expect(self.pg0, tx, self.pg0, len(tx) * 2)
1256
1257         fake_tx = []
1258         for p in tx:
1259             fake_tx.append(p)
1260             fake_tx.append(p)
1261         self.verify_capture_tunneled_ip4(
1262             self.pg0,
1263             rx,
1264             fake_tx,
1265             [VppMplsLabel(44), VppMplsLabel(46), VppMplsLabel(33, ttl=255)],
1266         )
1267
1268     def test_tunnel_uniform(self):
1269         """MPLS Tunnel Tests - Uniform"""
1270
1271         #
1272         # Create a tunnel with a single out label
1273         # The label stack is specified here from outer to inner
1274         #
1275         mpls_tun = VppMPLSTunnelInterface(
1276             self,
1277             [
1278                 VppRoutePath(
1279                     self.pg0.remote_ip4,
1280                     self.pg0.sw_if_index,
1281                     labels=[
1282                         VppMplsLabel(44, ttl=32),
1283                         VppMplsLabel(46, MplsLspMode.UNIFORM),
1284                     ],
1285                 )
1286             ],
1287         )
1288         mpls_tun.add_vpp_config()
1289         mpls_tun.admin_up()
1290
1291         #
1292         # add an unlabelled route through the new tunnel
1293         #
1294         route_10_0_0_3 = VppIpRoute(
1295             self, "10.0.0.3", 32, [VppRoutePath("0.0.0.0", mpls_tun._sw_if_index)]
1296         )
1297         route_10_0_0_3.add_vpp_config()
1298
1299         self.vapi.cli("clear trace")
1300         tx = self.create_stream_ip4(self.pg0, "10.0.0.3", ip_ttl=24)
1301         self.pg0.add_stream(tx)
1302
1303         self.pg_enable_capture(self.pg_interfaces)
1304         self.pg_start()
1305
1306         rx = self.pg0.get_capture()
1307         self.verify_capture_tunneled_ip4(
1308             self.pg0, rx, tx, [VppMplsLabel(44, ttl=32), VppMplsLabel(46, ttl=23)]
1309         )
1310
1311         #
1312         # add a labelled route through the new tunnel
1313         #
1314         route_10_0_0_4 = VppIpRoute(
1315             self,
1316             "10.0.0.4",
1317             32,
1318             [
1319                 VppRoutePath(
1320                     "0.0.0.0", mpls_tun._sw_if_index, labels=[VppMplsLabel(33, ttl=47)]
1321                 )
1322             ],
1323         )
1324         route_10_0_0_4.add_vpp_config()
1325
1326         self.vapi.cli("clear trace")
1327         tx = self.create_stream_ip4(self.pg0, "10.0.0.4")
1328         self.pg0.add_stream(tx)
1329
1330         self.pg_enable_capture(self.pg_interfaces)
1331         self.pg_start()
1332
1333         rx = self.pg0.get_capture()
1334         self.verify_capture_tunneled_ip4(
1335             self.pg0,
1336             rx,
1337             tx,
1338             [
1339                 VppMplsLabel(44, ttl=32),
1340                 VppMplsLabel(46, ttl=47),
1341                 VppMplsLabel(33, ttl=47),
1342             ],
1343         )
1344
1345     def test_mpls_tunnel_many(self):
1346         """MPLS Multiple Tunnels"""
1347
1348         for ii in range(100):
1349             mpls_tun = VppMPLSTunnelInterface(
1350                 self,
1351                 [
1352                     VppRoutePath(
1353                         self.pg0.remote_ip4,
1354                         self.pg0.sw_if_index,
1355                         labels=[
1356                             VppMplsLabel(44, ttl=32),
1357                             VppMplsLabel(46, MplsLspMode.UNIFORM),
1358                         ],
1359                     )
1360                 ],
1361             )
1362             mpls_tun.add_vpp_config()
1363             mpls_tun.admin_up()
1364         for ii in range(100):
1365             mpls_tun = VppMPLSTunnelInterface(
1366                 self,
1367                 [
1368                     VppRoutePath(
1369                         self.pg0.remote_ip4,
1370                         self.pg0.sw_if_index,
1371                         labels=[
1372                             VppMplsLabel(44, ttl=32),
1373                             VppMplsLabel(46, MplsLspMode.UNIFORM),
1374                         ],
1375                     )
1376                 ],
1377                 is_l2=1,
1378             )
1379             mpls_tun.add_vpp_config()
1380             mpls_tun.admin_up()
1381
1382     def test_v4_exp_null(self):
1383         """MPLS V4 Explicit NULL test"""
1384
1385         #
1386         # The first test case has an MPLS TTL of 0
1387         # all packet should be dropped
1388         #
1389         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(0, ttl=0)])
1390         self.send_and_assert_no_replies(self.pg0, tx, "MPLS TTL=0 packets forwarded")
1391
1392         #
1393         # a stream with a non-zero MPLS TTL
1394         # PG0 is in the default table
1395         #
1396         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(0)])
1397         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1398         self.verify_capture_ip4(self.pg0, rx, tx)
1399
1400         #
1401         # a stream with a non-zero MPLS TTL
1402         # PG1 is in table 1
1403         # we are ensuring the post-pop lookup occurs in the VRF table
1404         #
1405         tx = self.create_stream_labelled_ip4(self.pg1, [VppMplsLabel(0)])
1406         rx = self.send_and_expect(self.pg1, tx, self.pg1)
1407         self.verify_capture_ip4(self.pg1, rx, tx)
1408
1409     def test_v6_exp_null(self):
1410         """MPLS V6 Explicit NULL test"""
1411
1412         #
1413         # a stream with a non-zero MPLS TTL
1414         # PG0 is in the default table
1415         #
1416         tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(2)])
1417         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1418         self.verify_capture_ip6(self.pg0, rx, tx)
1419
1420         #
1421         # a stream with a non-zero MPLS TTL
1422         # PG1 is in table 1
1423         # we are ensuring the post-pop lookup occurs in the VRF table
1424         #
1425         tx = self.create_stream_labelled_ip6(self.pg1, [VppMplsLabel(2)])
1426         rx = self.send_and_expect(self.pg1, tx, self.pg1)
1427         self.verify_capture_ip6(self.pg0, rx, tx)
1428
1429     def test_deag(self):
1430         """MPLS Deagg"""
1431
1432         #
1433         # A de-agg route - next-hop lookup in default table
1434         #
1435         route_34_eos = VppMplsRoute(
1436             self, 34, 1, [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=0)]
1437         )
1438         route_34_eos.add_vpp_config()
1439
1440         #
1441         # ping an interface in the default table
1442         # PG0 is in the default table
1443         #
1444         tx = self.create_stream_labelled_ip4(
1445             self.pg0, [VppMplsLabel(34)], ping=1, ip_itf=self.pg0
1446         )
1447         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1448         self.verify_capture_ip4(self.pg0, rx, tx, ping_resp=1)
1449
1450         #
1451         # A de-agg route - next-hop lookup in non-default table
1452         #
1453         route_35_eos = VppMplsRoute(
1454             self, 35, 1, [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=1)]
1455         )
1456         route_35_eos.add_vpp_config()
1457         route_356_eos = VppMplsRoute(
1458             self,
1459             356,
1460             1,
1461             [VppRoutePath("0::0", 0xFFFFFFFF, nh_table_id=1)],
1462             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
1463         )
1464         route_356_eos.add_vpp_config()
1465
1466         #
1467         # ping an interface in the non-default table
1468         # PG0 is in the default table. packet arrive labelled in the
1469         # default table and egress unlabelled in the non-default
1470         #
1471         tx = self.create_stream_labelled_ip4(
1472             self.pg0, [VppMplsLabel(35)], ping=1, ip_itf=self.pg1
1473         )
1474         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1475         self.verify_capture_ip4(self.pg1, rx, tx, ping_resp=1)
1476         tx = self.create_stream_labelled_ip6(
1477             self.pg0, [VppMplsLabel(356)], ping=1, ip_itf=self.pg1
1478         )
1479         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1480         self.verify_capture_ip6(self.pg1, rx, tx, ping_resp=1)
1481
1482         #
1483         # Double pop
1484         #
1485         route_36_neos = VppMplsRoute(self, 36, 0, [VppRoutePath("0.0.0.0", 0xFFFFFFFF)])
1486         route_36_neos.add_vpp_config()
1487
1488         tx = self.create_stream_labelled_ip4(
1489             self.pg0, [VppMplsLabel(36), VppMplsLabel(35)], ping=1, ip_itf=self.pg1
1490         )
1491         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1492         self.verify_capture_ip4(self.pg1, rx, tx, ping_resp=1)
1493
1494         route_36_neos.remove_vpp_config()
1495         route_35_eos.remove_vpp_config()
1496         route_34_eos.remove_vpp_config()
1497
1498     def test_interface_rx(self):
1499         """MPLS Interface Receive"""
1500
1501         #
1502         # Add a non-recursive route that will forward the traffic
1503         # post-interface-rx
1504         #
1505         route_10_0_0_1 = VppIpRoute(
1506             self,
1507             "10.0.0.1",
1508             32,
1509             table_id=1,
1510             paths=[VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)],
1511         )
1512         route_10_0_0_1.add_vpp_config()
1513
1514         #
1515         # An interface receive label that maps traffic to RX on interface
1516         # pg1
1517         # by injecting the packet in on pg0, which is in table 0
1518         # doing an interface-rx on pg1 and matching a route in table 1
1519         # if the packet egresses, then we must have swapped to pg1
1520         # so as to have matched the route in table 1
1521         #
1522         route_34_eos = VppMplsRoute(
1523             self,
1524             34,
1525             1,
1526             [
1527                 VppRoutePath(
1528                     "0.0.0.0",
1529                     self.pg1.sw_if_index,
1530                     type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
1531                 )
1532             ],
1533         )
1534         route_34_eos.add_vpp_config()
1535
1536         #
1537         # ping an interface in the default table
1538         # PG0 is in the default table
1539         #
1540         tx = self.create_stream_labelled_ip4(
1541             self.pg0, [VppMplsLabel(34)], dst_ip="10.0.0.1"
1542         )
1543         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1544         self.verify_capture_ip4(self.pg1, rx, tx)
1545
1546     def test_mcast_mid_point(self):
1547         """MPLS Multicast Mid Point"""
1548
1549         #
1550         # Add a non-recursive route that will forward the traffic
1551         # post-interface-rx
1552         #
1553         route_10_0_0_1 = VppIpRoute(
1554             self,
1555             "10.0.0.1",
1556             32,
1557             table_id=1,
1558             paths=[VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)],
1559         )
1560         route_10_0_0_1.add_vpp_config()
1561
1562         #
1563         # Add a mcast entry that replicate to pg2 and pg3
1564         # and replicate to a interface-rx (like a bud node would)
1565         #
1566         route_3400_eos = VppMplsRoute(
1567             self,
1568             3400,
1569             1,
1570             [
1571                 VppRoutePath(
1572                     self.pg2.remote_ip4,
1573                     self.pg2.sw_if_index,
1574                     labels=[VppMplsLabel(3401)],
1575                 ),
1576                 VppRoutePath(
1577                     self.pg3.remote_ip4,
1578                     self.pg3.sw_if_index,
1579                     labels=[VppMplsLabel(3402)],
1580                 ),
1581                 VppRoutePath(
1582                     "0.0.0.0",
1583                     self.pg1.sw_if_index,
1584                     type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
1585                 ),
1586             ],
1587             is_multicast=1,
1588         )
1589         route_3400_eos.add_vpp_config()
1590
1591         #
1592         # ping an interface in the default table
1593         # PG0 is in the default table
1594         #
1595         self.vapi.cli("clear trace")
1596         tx = self.create_stream_labelled_ip4(
1597             self.pg0, [VppMplsLabel(3400, ttl=64)], n=257, dst_ip="10.0.0.1"
1598         )
1599         self.pg0.add_stream(tx)
1600
1601         self.pg_enable_capture(self.pg_interfaces)
1602         self.pg_start()
1603
1604         rx = self.pg1.get_capture(257)
1605         self.verify_capture_ip4(self.pg1, rx, tx)
1606
1607         rx = self.pg2.get_capture(257)
1608         self.verify_capture_labelled(self.pg2, rx, tx, [VppMplsLabel(3401, ttl=63)])
1609         rx = self.pg3.get_capture(257)
1610         self.verify_capture_labelled(self.pg3, rx, tx, [VppMplsLabel(3402, ttl=63)])
1611
1612     def test_mcast_head(self):
1613         """MPLS Multicast Head-end"""
1614
1615         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
1616         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
1617
1618         #
1619         # Create a multicast tunnel with two replications
1620         #
1621         mpls_tun = VppMPLSTunnelInterface(
1622             self,
1623             [
1624                 VppRoutePath(
1625                     self.pg2.remote_ip4, self.pg2.sw_if_index, labels=[VppMplsLabel(42)]
1626                 ),
1627                 VppRoutePath(
1628                     self.pg3.remote_ip4, self.pg3.sw_if_index, labels=[VppMplsLabel(43)]
1629                 ),
1630             ],
1631             is_multicast=1,
1632         )
1633         mpls_tun.add_vpp_config()
1634         mpls_tun.admin_up()
1635
1636         #
1637         # add an unlabelled route through the new tunnel
1638         #
1639         route_10_0_0_3 = VppIpRoute(
1640             self, "10.0.0.3", 32, [VppRoutePath("0.0.0.0", mpls_tun._sw_if_index)]
1641         )
1642         route_10_0_0_3.add_vpp_config()
1643
1644         self.vapi.cli("clear trace")
1645         tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
1646         self.pg0.add_stream(tx)
1647
1648         self.pg_enable_capture(self.pg_interfaces)
1649         self.pg_start()
1650
1651         rx = self.pg2.get_capture(257)
1652         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(42)])
1653         rx = self.pg3.get_capture(257)
1654         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(43)])
1655
1656         #
1657         # An an IP multicast route via the tunnel
1658         # A (*,G).
1659         # one accepting interface, pg0, 1 forwarding interface via the tunnel
1660         #
1661         route_232_1_1_1 = VppIpMRoute(
1662             self,
1663             "0.0.0.0",
1664             "232.1.1.1",
1665             32,
1666             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
1667             [
1668                 VppMRoutePath(
1669                     self.pg0.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT
1670                 ),
1671                 VppMRoutePath(
1672                     mpls_tun._sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD
1673                 ),
1674             ],
1675         )
1676         route_232_1_1_1.add_vpp_config()
1677         self.logger.info(self.vapi.cli("sh ip mfib index 0"))
1678
1679         self.vapi.cli("clear trace")
1680         tx = self.create_stream_ip4(self.pg0, "232.1.1.1")
1681         self.pg0.add_stream(tx)
1682
1683         self.pg_enable_capture(self.pg_interfaces)
1684         self.pg_start()
1685
1686         rx = self.pg2.get_capture(257)
1687         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(42)])
1688         rx = self.pg3.get_capture(257)
1689         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(43)])
1690
1691     def test_mcast_ip4_tail(self):
1692         """MPLS IPv4 Multicast Tail"""
1693
1694         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
1695         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
1696
1697         #
1698         # Add a multicast route that will forward the traffic
1699         # post-disposition
1700         #
1701         route_232_1_1_1 = VppIpMRoute(
1702             self,
1703             "0.0.0.0",
1704             "232.1.1.1",
1705             32,
1706             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
1707             table_id=1,
1708             paths=[
1709                 VppMRoutePath(
1710                     self.pg1.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD
1711                 )
1712             ],
1713         )
1714         route_232_1_1_1.add_vpp_config()
1715
1716         #
1717         # An interface receive label that maps traffic to RX on interface
1718         # pg1
1719         # by injecting the packet in on pg0, which is in table 0
1720         # doing an rpf-id  and matching a route in table 1
1721         # if the packet egresses, then we must have matched the route in
1722         # table 1
1723         #
1724         route_34_eos = VppMplsRoute(
1725             self,
1726             34,
1727             1,
1728             [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=1, rpf_id=55)],
1729             is_multicast=1,
1730             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
1731         )
1732
1733         route_34_eos.add_vpp_config()
1734
1735         #
1736         # Drop due to interface lookup miss
1737         #
1738         self.vapi.cli("clear trace")
1739         tx = self.create_stream_labelled_ip4(
1740             self.pg0, [VppMplsLabel(34)], dst_ip="232.1.1.1", n=1
1741         )
1742         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop none")
1743
1744         #
1745         # set the RPF-ID of the entry to match the input packet's
1746         #
1747         route_232_1_1_1.update_rpf_id(55)
1748         self.logger.info(self.vapi.cli("sh ip mfib index 1 232.1.1.1"))
1749
1750         tx = self.create_stream_labelled_ip4(
1751             self.pg0, [VppMplsLabel(34)], dst_ip="232.1.1.1"
1752         )
1753         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1754         self.verify_capture_ip4(self.pg1, rx, tx)
1755
1756         #
1757         # disposed packets have an invalid IPv4 checksum
1758         #
1759         tx = self.create_stream_labelled_ip4(
1760             self.pg0, [VppMplsLabel(34)], dst_ip="232.1.1.1", n=65, chksum=1
1761         )
1762         self.send_and_assert_no_replies(self.pg0, tx, "Invalid Checksum")
1763
1764         #
1765         # set the RPF-ID of the entry to not match the input packet's
1766         #
1767         route_232_1_1_1.update_rpf_id(56)
1768         tx = self.create_stream_labelled_ip4(
1769             self.pg0, [VppMplsLabel(34)], dst_ip="232.1.1.1"
1770         )
1771         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
1772
1773     def test_mcast_ip6_tail(self):
1774         """MPLS IPv6 Multicast Tail"""
1775
1776         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
1777         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
1778
1779         #
1780         # Add a multicast route that will forward the traffic
1781         # post-disposition
1782         #
1783         route_ff = VppIpMRoute(
1784             self,
1785             "::",
1786             "ff01::1",
1787             32,
1788             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
1789             table_id=1,
1790             paths=[
1791                 VppMRoutePath(
1792                     self.pg1.sw_if_index,
1793                     MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
1794                     proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
1795                 )
1796             ],
1797         )
1798         route_ff.add_vpp_config()
1799
1800         #
1801         # An interface receive label that maps traffic to RX on interface
1802         # pg1
1803         # by injecting the packet in on pg0, which is in table 0
1804         # doing an rpf-id  and matching a route in table 1
1805         # if the packet egresses, then we must have matched the route in
1806         # table 1
1807         #
1808         route_34_eos = VppMplsRoute(
1809             self,
1810             34,
1811             1,
1812             [VppRoutePath("::", 0xFFFFFFFF, nh_table_id=1, rpf_id=55)],
1813             is_multicast=1,
1814             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
1815         )
1816
1817         route_34_eos.add_vpp_config()
1818
1819         #
1820         # Drop due to interface lookup miss
1821         #
1822         tx = self.create_stream_labelled_ip6(
1823             self.pg0, [VppMplsLabel(34)], dst_ip="ff01::1"
1824         )
1825         self.send_and_assert_no_replies(self.pg0, tx, "RPF Miss")
1826
1827         #
1828         # set the RPF-ID of the entry to match the input packet's
1829         #
1830         route_ff.update_rpf_id(55)
1831
1832         tx = self.create_stream_labelled_ip6(
1833             self.pg0, [VppMplsLabel(34)], dst_ip="ff01::1"
1834         )
1835         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1836         self.verify_capture_ip6(self.pg1, rx, tx)
1837
1838         #
1839         # disposed packets have hop-limit = 1
1840         #
1841         tx = self.create_stream_labelled_ip6(
1842             self.pg0, [VppMplsLabel(34)], dst_ip="ff01::1", hlim=1
1843         )
1844         rx = self.send_and_expect_some(self.pg0, tx, self.pg0)
1845         self.verify_capture_ip6_icmp(self.pg0, rx, tx)
1846
1847         #
1848         # set the RPF-ID of the entry to not match the input packet's
1849         #
1850         route_ff.update_rpf_id(56)
1851         tx = self.create_stream_labelled_ip6(
1852             self.pg0, [VppMplsLabel(34)], dst_ip="ff01::1"
1853         )
1854         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
1855
1856     def test_6pe(self):
1857         """MPLS 6PE"""
1858
1859         #
1860         # Add a non-recursive route with a single out label
1861         #
1862         route_10_0_0_1 = VppIpRoute(
1863             self,
1864             "10.0.0.1",
1865             32,
1866             [
1867                 VppRoutePath(
1868                     self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[VppMplsLabel(45)]
1869                 )
1870             ],
1871         )
1872         route_10_0_0_1.add_vpp_config()
1873
1874         # bind a local label to the route
1875         binding = VppMplsIpBind(self, 44, "10.0.0.1", 32)
1876         binding.add_vpp_config()
1877
1878         #
1879         # a labelled v6 route that resolves through the v4
1880         #
1881         route_2001_3 = VppIpRoute(
1882             self,
1883             "2001::3",
1884             128,
1885             [VppRoutePath("10.0.0.1", INVALID_INDEX, labels=[VppMplsLabel(32)])],
1886         )
1887         route_2001_3.add_vpp_config()
1888
1889         tx = self.create_stream_ip6(self.pg0, "2001::3")
1890         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1891
1892         self.verify_capture_labelled_ip6(
1893             self.pg0, rx, tx, [VppMplsLabel(45), VppMplsLabel(32)]
1894         )
1895
1896         #
1897         # and a v4 recursive via the v6
1898         #
1899         route_20_3 = VppIpRoute(
1900             self,
1901             "20.0.0.3",
1902             32,
1903             [VppRoutePath("2001::3", INVALID_INDEX, labels=[VppMplsLabel(99)])],
1904         )
1905         route_20_3.add_vpp_config()
1906
1907         tx = self.create_stream_ip4(self.pg0, "20.0.0.3")
1908         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1909
1910         self.verify_capture_labelled_ip4(
1911             self.pg0, rx, tx, [VppMplsLabel(45), VppMplsLabel(32), VppMplsLabel(99)]
1912         )
1913
1914     def test_attached(self):
1915         """Attach Routes with Local Label"""
1916
1917         #
1918         # test that if a local label is associated with an attached/connected
1919         # prefix, that we can reach hosts in the prefix.
1920         #
1921         binding = VppMplsIpBind(
1922             self, 44, self.pg0._local_ip4_subnet, self.pg0.local_ip4_prefix_len
1923         )
1924         binding.add_vpp_config()
1925
1926         tx = (
1927             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
1928             / MPLS(label=44, ttl=64)
1929             / IP(src=self.pg0.remote_ip4, dst=self.pg0.remote_ip4)
1930             / UDP(sport=1234, dport=1234)
1931             / Raw(b"\xa5" * 100)
1932         )
1933         rxs = self.send_and_expect(self.pg0, [tx], self.pg0)
1934         for rx in rxs:
1935             # if there's an ARP then the label is linked to the glean
1936             # which is wrong.
1937             self.assertFalse(rx.haslayer(ARP))
1938             # it should be unicasted to the host
1939             self.assertEqual(rx[Ether].dst, self.pg0.remote_mac)
1940             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1941
1942
1943 class TestMPLSDisabled(VppTestCase):
1944     """MPLS disabled"""
1945
1946     @classmethod
1947     def setUpClass(cls):
1948         super(TestMPLSDisabled, cls).setUpClass()
1949
1950     @classmethod
1951     def tearDownClass(cls):
1952         super(TestMPLSDisabled, cls).tearDownClass()
1953
1954     def setUp(self):
1955         super(TestMPLSDisabled, self).setUp()
1956
1957         # create 2 pg interfaces
1958         self.create_pg_interfaces(range(2))
1959
1960         self.tbl = VppMplsTable(self, 0)
1961         self.tbl.add_vpp_config()
1962
1963         # PG0 is MPLS enabled
1964         self.pg0.admin_up()
1965         self.pg0.config_ip4()
1966         self.pg0.resolve_arp()
1967         self.pg0.enable_mpls()
1968
1969         # PG 1 is not MPLS enabled
1970         self.pg1.admin_up()
1971
1972     def tearDown(self):
1973         for i in self.pg_interfaces:
1974             i.unconfig_ip4()
1975             i.admin_down()
1976
1977         self.pg0.disable_mpls()
1978         super(TestMPLSDisabled, self).tearDown()
1979
1980     def test_mpls_disabled(self):
1981         """MPLS Disabled"""
1982
1983         self.logger.info(self.vapi.cli("show mpls interface"))
1984         self.logger.info(self.vapi.cli("show mpls interface pg1"))
1985         self.logger.info(self.vapi.cli("show mpls interface pg0"))
1986
1987         tx = (
1988             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
1989             / MPLS(label=32, ttl=64)
1990             / IPv6(src="2001::1", dst=self.pg0.remote_ip6)
1991             / UDP(sport=1234, dport=1234)
1992             / Raw(b"\xa5" * 100)
1993         )
1994
1995         #
1996         # A simple MPLS xconnect - eos label in label out
1997         #
1998         route_32_eos = VppMplsRoute(
1999             self,
2000             32,
2001             1,
2002             [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[33])],
2003         )
2004         route_32_eos.add_vpp_config()
2005
2006         #
2007         # PG1 does not forward IP traffic
2008         #
2009         self.send_and_assert_no_replies(self.pg1, tx, "MPLS disabled")
2010
2011         #
2012         # MPLS enable PG1
2013         #
2014         self.pg1.enable_mpls()
2015
2016         self.logger.info(self.vapi.cli("show mpls interface"))
2017         self.logger.info(self.vapi.cli("show mpls interface pg1"))
2018
2019         #
2020         # Now we get packets through
2021         #
2022         self.pg1.add_stream(tx)
2023         self.pg_enable_capture(self.pg_interfaces)
2024         self.pg_start()
2025
2026         rx = self.pg0.get_capture(1)
2027
2028         #
2029         # Disable PG1
2030         #
2031         self.pg1.disable_mpls()
2032
2033         #
2034         # PG1 does not forward IP traffic
2035         #
2036         self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
2037         self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
2038
2039
2040 class TestMPLSPIC(VppTestCase):
2041     """MPLS Prefix-Independent Convergence (PIC) edge convergence"""
2042
2043     @classmethod
2044     def setUpClass(cls):
2045         super(TestMPLSPIC, cls).setUpClass()
2046
2047     @classmethod
2048     def tearDownClass(cls):
2049         super(TestMPLSPIC, cls).tearDownClass()
2050
2051     def setUp(self):
2052         super(TestMPLSPIC, self).setUp()
2053
2054         # create 2 pg interfaces
2055         self.create_pg_interfaces(range(4))
2056
2057         mpls_tbl = VppMplsTable(self, 0)
2058         mpls_tbl.add_vpp_config()
2059         tbl4 = VppIpTable(self, 1)
2060         tbl4.add_vpp_config()
2061         tbl6 = VppIpTable(self, 1, is_ip6=1)
2062         tbl6.add_vpp_config()
2063
2064         # core links
2065         self.pg0.admin_up()
2066         self.pg0.config_ip4()
2067         self.pg0.resolve_arp()
2068         self.pg0.enable_mpls()
2069
2070         self.pg1.admin_up()
2071         self.pg1.config_ip4()
2072         self.pg1.resolve_arp()
2073         self.pg1.enable_mpls()
2074
2075         # VRF (customer facing) link
2076         self.pg2.admin_up()
2077         self.pg2.set_table_ip4(1)
2078         self.pg2.config_ip4()
2079         self.pg2.resolve_arp()
2080         self.pg2.set_table_ip6(1)
2081         self.pg2.config_ip6()
2082         self.pg2.resolve_ndp()
2083
2084         self.pg3.admin_up()
2085         self.pg3.set_table_ip4(1)
2086         self.pg3.config_ip4()
2087         self.pg3.resolve_arp()
2088         self.pg3.set_table_ip6(1)
2089         self.pg3.config_ip6()
2090         self.pg3.resolve_ndp()
2091
2092     def tearDown(self):
2093         self.pg0.disable_mpls()
2094         self.pg1.disable_mpls()
2095         for i in self.pg_interfaces:
2096             i.unconfig_ip4()
2097             i.unconfig_ip6()
2098             i.set_table_ip4(0)
2099             i.set_table_ip6(0)
2100             i.admin_down()
2101         super(TestMPLSPIC, self).tearDown()
2102
2103     def test_mpls_ibgp_pic(self):
2104         """MPLS iBGP Prefix-Independent Convergence (PIC) edge convergence
2105
2106         1) setup many iBGP VPN routes via a pair of iBGP peers.
2107         2) Check EMCP forwarding to these peers
2108         3) withdraw the IGP route to one of these peers.
2109         4) check forwarding continues to the remaining peer
2110         """
2111
2112         #
2113         # IGP+LDP core routes
2114         #
2115         core_10_0_0_45 = VppIpRoute(
2116             self,
2117             "10.0.0.45",
2118             32,
2119             [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[45])],
2120         )
2121         core_10_0_0_45.add_vpp_config()
2122
2123         core_10_0_0_46 = VppIpRoute(
2124             self,
2125             "10.0.0.46",
2126             32,
2127             [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index, labels=[46])],
2128         )
2129         core_10_0_0_46.add_vpp_config()
2130
2131         #
2132         # Lot's of VPN routes. We need more the 64 so VPP will build
2133         # the fast convergence indirection
2134         #
2135         vpn_routes = []
2136         pkts = []
2137         for ii in range(NUM_PKTS):
2138             dst = "192.168.1.%d" % ii
2139             vpn_routes.append(
2140                 VppIpRoute(
2141                     self,
2142                     dst,
2143                     32,
2144                     [
2145                         VppRoutePath(
2146                             "10.0.0.45",
2147                             0xFFFFFFFF,
2148                             labels=[145],
2149                             flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_HOST,
2150                         ),
2151                         VppRoutePath(
2152                             "10.0.0.46",
2153                             0xFFFFFFFF,
2154                             labels=[146],
2155                             flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_HOST,
2156                         ),
2157                     ],
2158                     table_id=1,
2159                 )
2160             )
2161             vpn_routes[ii].add_vpp_config()
2162
2163             pkts.append(
2164                 Ether(dst=self.pg2.local_mac, src=self.pg2.remote_mac)
2165                 / IP(src=self.pg2.remote_ip4, dst=dst)
2166                 / UDP(sport=1234, dport=1234)
2167                 / Raw(b"\xa5" * 100)
2168             )
2169
2170         #
2171         # Send the packet stream (one pkt to each VPN route)
2172         #  - expect a 50-50 split of the traffic
2173         #
2174         self.pg2.add_stream(pkts)
2175         self.pg_enable_capture(self.pg_interfaces)
2176         self.pg_start()
2177
2178         rx0 = self.pg0._get_capture(NUM_PKTS)
2179         rx1 = self.pg1._get_capture(NUM_PKTS)
2180
2181         # not testing the LB hashing algorithm so we're not concerned
2182         # with the split ratio, just as long as neither is 0
2183         self.assertNotEqual(0, len(rx0))
2184         self.assertNotEqual(0, len(rx1))
2185         self.assertEqual(
2186             len(pkts),
2187             len(rx0) + len(rx1),
2188             "Expected all (%s) packets across both ECMP paths. "
2189             "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)),
2190         )
2191
2192         #
2193         # use a test CLI command to stop the FIB walk process, this
2194         # will prevent the FIB converging the VPN routes and thus allow
2195         # us to probe the interim (post-fail, pre-converge) state
2196         #
2197         self.vapi.ppcli("test fib-walk-process disable")
2198
2199         #
2200         # Withdraw one of the IGP routes
2201         #
2202         core_10_0_0_46.remove_vpp_config()
2203
2204         #
2205         # now all packets should be forwarded through the remaining peer
2206         #
2207         self.vapi.ppcli("clear trace")
2208         self.pg2.add_stream(pkts)
2209         self.pg_enable_capture(self.pg_interfaces)
2210         self.pg_start()
2211
2212         rx0 = self.pg0.get_capture(NUM_PKTS)
2213         self.assertEqual(
2214             len(pkts),
2215             len(rx0),
2216             "Expected all (%s) packets across single path. "
2217             "rx0: %s." % (len(pkts), len(rx0)),
2218         )
2219
2220         #
2221         # enable the FIB walk process to converge the FIB
2222         #
2223         self.vapi.ppcli("test fib-walk-process enable")
2224
2225         #
2226         # packets should still be forwarded through the remaining peer
2227         #
2228         self.pg2.add_stream(pkts)
2229         self.pg_enable_capture(self.pg_interfaces)
2230         self.pg_start()
2231
2232         rx0 = self.pg0.get_capture(NUM_PKTS)
2233         self.assertEqual(
2234             len(pkts),
2235             len(rx0),
2236             "Expected all (%s) packets across single path. "
2237             "rx0: %s." % (len(pkts), len(rx0)),
2238         )
2239
2240         #
2241         # Add the IGP route back and we return to load-balancing
2242         #
2243         core_10_0_0_46.add_vpp_config()
2244
2245         self.pg2.add_stream(pkts)
2246         self.pg_enable_capture(self.pg_interfaces)
2247         self.pg_start()
2248
2249         rx0 = self.pg0._get_capture(NUM_PKTS)
2250         rx1 = self.pg1._get_capture(NUM_PKTS)
2251         self.assertNotEqual(0, len(rx0))
2252         self.assertNotEqual(0, len(rx1))
2253         self.assertEqual(
2254             len(pkts),
2255             len(rx0) + len(rx1),
2256             "Expected all (%s) packets across both ECMP paths. "
2257             "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)),
2258         )
2259
2260     def test_mpls_ebgp_pic(self):
2261         """MPLS eBGP Prefix-Independent Convergence (PIC) edge convergence
2262
2263         1) setup many eBGP VPN routes via a pair of eBGP peers.
2264         2) Check EMCP forwarding to these peers
2265         3) withdraw one eBGP path - expect LB across remaining eBGP
2266         """
2267
2268         #
2269         # Lot's of VPN routes. We need more the 64 so VPP will build
2270         # the fast convergence indirection
2271         #
2272         vpn_routes = []
2273         vpn_bindings = []
2274         pkts = []
2275         for ii in range(NUM_PKTS):
2276             dst = "192.168.1.%d" % ii
2277             local_label = 1600 + ii
2278             vpn_routes.append(
2279                 VppIpRoute(
2280                     self,
2281                     dst,
2282                     32,
2283                     [
2284                         VppRoutePath(
2285                             self.pg2.remote_ip4,
2286                             0xFFFFFFFF,
2287                             nh_table_id=1,
2288                             flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED,
2289                         ),
2290                         VppRoutePath(
2291                             self.pg3.remote_ip4,
2292                             0xFFFFFFFF,
2293                             nh_table_id=1,
2294                             flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED,
2295                         ),
2296                     ],
2297                     table_id=1,
2298                 )
2299             )
2300             vpn_routes[ii].add_vpp_config()
2301
2302             vpn_bindings.append(
2303                 VppMplsIpBind(self, local_label, dst, 32, ip_table_id=1)
2304             )
2305             vpn_bindings[ii].add_vpp_config()
2306
2307             pkts.append(
2308                 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2309                 / MPLS(label=local_label, ttl=64)
2310                 / IP(src=self.pg0.remote_ip4, dst=dst)
2311                 / UDP(sport=1234, dport=1234)
2312                 / Raw(b"\xa5" * 100)
2313             )
2314
2315         #
2316         # Send the packet stream (one pkt to each VPN route)
2317         #  - expect a 50-50 split of the traffic
2318         #
2319         self.pg0.add_stream(pkts)
2320         self.pg_enable_capture(self.pg_interfaces)
2321         self.pg_start()
2322
2323         rx0 = self.pg2._get_capture(NUM_PKTS)
2324         rx1 = self.pg3._get_capture(NUM_PKTS)
2325
2326         # not testing the LB hashing algorithm so we're not concerned
2327         # with the split ratio, just as long as neither is 0
2328         self.assertNotEqual(0, len(rx0))
2329         self.assertNotEqual(0, len(rx1))
2330         self.assertEqual(
2331             len(pkts),
2332             len(rx0) + len(rx1),
2333             "Expected all (%s) packets across both ECMP paths. "
2334             "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)),
2335         )
2336
2337         #
2338         # use a test CLI command to stop the FIB walk process, this
2339         # will prevent the FIB converging the VPN routes and thus allow
2340         # us to probe the interim (post-fail, pre-converge) state
2341         #
2342         self.vapi.ppcli("test fib-walk-process disable")
2343
2344         #
2345         # withdraw the connected prefix on the interface.
2346         #
2347         self.pg2.unconfig_ip4()
2348
2349         #
2350         # now all packets should be forwarded through the remaining peer
2351         #
2352         self.pg0.add_stream(pkts)
2353         self.pg_enable_capture(self.pg_interfaces)
2354         self.pg_start()
2355
2356         rx0 = self.pg3.get_capture(NUM_PKTS)
2357         self.assertEqual(
2358             len(pkts),
2359             len(rx0),
2360             "Expected all (%s) packets across single path. "
2361             "rx0: %s." % (len(pkts), len(rx0)),
2362         )
2363
2364         #
2365         # enable the FIB walk process to converge the FIB
2366         #
2367         self.vapi.ppcli("test fib-walk-process enable")
2368
2369         #
2370         # packets should still be forwarded through the remaining peer
2371         #
2372         self.pg0.add_stream(pkts)
2373         self.pg_enable_capture(self.pg_interfaces)
2374         self.pg_start()
2375
2376         rx0 = self.pg3.get_capture(NUM_PKTS)
2377         self.assertEqual(
2378             len(pkts),
2379             len(rx0),
2380             "Expected all (%s) packets across single path. "
2381             "rx0: %s." % (len(pkts), len(rx0)),
2382         )
2383
2384         #
2385         # put the connected routes back
2386         #
2387         self.pg2.config_ip4()
2388         self.pg2.resolve_arp()
2389
2390         self.pg0.add_stream(pkts)
2391         self.pg_enable_capture(self.pg_interfaces)
2392         self.pg_start()
2393
2394         rx0 = self.pg2._get_capture(NUM_PKTS)
2395         rx1 = self.pg3._get_capture(NUM_PKTS)
2396         self.assertNotEqual(0, len(rx0))
2397         self.assertNotEqual(0, len(rx1))
2398         self.assertEqual(
2399             len(pkts),
2400             len(rx0) + len(rx1),
2401             "Expected all (%s) packets across both ECMP paths. "
2402             "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)),
2403         )
2404
2405     def test_mpls_v6_ebgp_pic(self):
2406         """MPLSv6 eBGP Prefix-Independent Convergence (PIC) edge convergence
2407
2408         1) setup many eBGP VPNv6 routes via a pair of eBGP peers
2409         2) Check EMCP forwarding to these peers
2410         3) withdraw one eBGP path - expect LB across remaining eBGP
2411         """
2412
2413         #
2414         # Lot's of VPN routes. We need more the 64 so VPP will build
2415         # the fast convergence indirection
2416         #
2417         vpn_routes = []
2418         vpn_bindings = []
2419         pkts = []
2420         for ii in range(NUM_PKTS):
2421             dst = "3000::%d" % ii
2422             local_label = 1600 + ii
2423             vpn_routes.append(
2424                 VppIpRoute(
2425                     self,
2426                     dst,
2427                     128,
2428                     [
2429                         VppRoutePath(
2430                             self.pg2.remote_ip6,
2431                             0xFFFFFFFF,
2432                             nh_table_id=1,
2433                             flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED,
2434                         ),
2435                         VppRoutePath(
2436                             self.pg3.remote_ip6,
2437                             0xFFFFFFFF,
2438                             nh_table_id=1,
2439                             flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED,
2440                         ),
2441                     ],
2442                     table_id=1,
2443                 )
2444             )
2445             vpn_routes[ii].add_vpp_config()
2446
2447             vpn_bindings.append(
2448                 VppMplsIpBind(self, local_label, dst, 128, ip_table_id=1)
2449             )
2450             vpn_bindings[ii].add_vpp_config()
2451
2452             pkts.append(
2453                 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2454                 / MPLS(label=local_label, ttl=64)
2455                 / IPv6(src=self.pg0.remote_ip6, dst=dst)
2456                 / UDP(sport=1234, dport=1234)
2457                 / Raw(b"\xa5" * 100)
2458             )
2459             self.logger.info(self.vapi.cli("sh ip6 fib %s" % dst))
2460
2461         self.pg0.add_stream(pkts)
2462         self.pg_enable_capture(self.pg_interfaces)
2463         self.pg_start()
2464
2465         rx0 = self.pg2._get_capture(NUM_PKTS)
2466         rx1 = self.pg3._get_capture(NUM_PKTS)
2467         self.assertNotEqual(0, len(rx0))
2468         self.assertNotEqual(0, len(rx1))
2469         self.assertEqual(
2470             len(pkts),
2471             len(rx0) + len(rx1),
2472             "Expected all (%s) packets across both ECMP paths. "
2473             "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)),
2474         )
2475
2476         #
2477         # use a test CLI command to stop the FIB walk process, this
2478         # will prevent the FIB converging the VPN routes and thus allow
2479         # us to probe the interim (post-fail, pre-converge) state
2480         #
2481         self.vapi.ppcli("test fib-walk-process disable")
2482
2483         #
2484         # withdraw the connected prefix on the interface.
2485         # and shutdown the interface so the ND cache is flushed.
2486         #
2487         self.pg2.unconfig_ip6()
2488         self.pg2.admin_down()
2489
2490         #
2491         # now all packets should be forwarded through the remaining peer
2492         #
2493         self.pg0.add_stream(pkts)
2494         self.pg_enable_capture(self.pg_interfaces)
2495         self.pg_start()
2496
2497         rx0 = self.pg3.get_capture(NUM_PKTS)
2498         self.assertEqual(
2499             len(pkts),
2500             len(rx0),
2501             "Expected all (%s) packets across single path. "
2502             "rx0: %s." % (len(pkts), len(rx0)),
2503         )
2504
2505         #
2506         # enable the FIB walk process to converge the FIB
2507         #
2508         self.vapi.ppcli("test fib-walk-process enable")
2509         self.pg0.add_stream(pkts)
2510         self.pg_enable_capture(self.pg_interfaces)
2511         self.pg_start()
2512
2513         rx0 = self.pg3.get_capture(NUM_PKTS)
2514         self.assertEqual(
2515             len(pkts),
2516             len(rx0),
2517             "Expected all (%s) packets across single path. "
2518             "rx0: %s." % (len(pkts), len(rx0)),
2519         )
2520
2521         #
2522         # put the connected routes back
2523         #
2524         self.logger.info(self.vapi.cli("sh log"))
2525         self.pg2.admin_up()
2526         self.pg2.config_ip6()
2527         self.pg2.resolve_ndp()
2528
2529         self.pg0.add_stream(pkts)
2530         self.pg_enable_capture(self.pg_interfaces)
2531         self.pg_start()
2532
2533         rx0 = self.pg2._get_capture(NUM_PKTS)
2534         rx1 = self.pg3._get_capture(NUM_PKTS)
2535         self.assertNotEqual(0, len(rx0))
2536         self.assertNotEqual(0, len(rx1))
2537         self.assertEqual(
2538             len(pkts),
2539             len(rx0) + len(rx1),
2540             "Expected all (%s) packets across both ECMP paths. "
2541             "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)),
2542         )
2543
2544
2545 class TestMPLSL2(VppTestCase):
2546     """MPLS-L2"""
2547
2548     @classmethod
2549     def setUpClass(cls):
2550         super(TestMPLSL2, cls).setUpClass()
2551
2552     @classmethod
2553     def tearDownClass(cls):
2554         super(TestMPLSL2, cls).tearDownClass()
2555
2556     def setUp(self):
2557         super(TestMPLSL2, self).setUp()
2558
2559         # create 2 pg interfaces
2560         self.create_pg_interfaces(range(2))
2561
2562         # create the default MPLS table
2563         self.tables = []
2564         tbl = VppMplsTable(self, 0)
2565         tbl.add_vpp_config()
2566         self.tables.append(tbl)
2567
2568         # use pg0 as the core facing interface, don't resolve ARP
2569         self.pg0.admin_up()
2570         self.pg0.config_ip4()
2571         self.pg0.enable_mpls()
2572
2573         # use the other 2 for customer facing L2 links
2574         for i in self.pg_interfaces[1:]:
2575             i.admin_up()
2576
2577     def tearDown(self):
2578         for i in self.pg_interfaces[1:]:
2579             i.admin_down()
2580
2581         self.pg0.disable_mpls()
2582         self.pg0.unconfig_ip4()
2583         self.pg0.admin_down()
2584         super(TestMPLSL2, self).tearDown()
2585
2586     def verify_capture_tunneled_ethernet(self, capture, sent, mpls_labels):
2587         capture = verify_filter(capture, sent)
2588
2589         self.assertEqual(len(capture), len(sent))
2590
2591         for i in range(len(capture)):
2592             tx = sent[i]
2593             rx = capture[i]
2594
2595             # the MPLS TTL is 255 since it enters a new tunnel
2596             verify_mpls_stack(self, rx, mpls_labels)
2597
2598             tx_eth = tx[Ether]
2599             rx_eth = Ether(scapy.compat.raw(rx[MPLS].payload))
2600
2601             self.assertEqual(rx_eth.src, tx_eth.src)
2602             self.assertEqual(rx_eth.dst, tx_eth.dst)
2603
2604     def verify_arp_req(self, rx, smac, sip, dip):
2605         ether = rx[Ether]
2606         self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
2607         self.assertEqual(ether.src, smac)
2608
2609         arp = rx[ARP]
2610         self.assertEqual(arp.hwtype, 1)
2611         self.assertEqual(arp.ptype, 0x800)
2612         self.assertEqual(arp.hwlen, 6)
2613         self.assertEqual(arp.plen, 4)
2614         self.assertEqual(arp.op, ARP.who_has)
2615         self.assertEqual(arp.hwsrc, smac)
2616         self.assertEqual(arp.hwdst, "00:00:00:00:00:00")
2617         self.assertEqual(arp.psrc, sip)
2618         self.assertEqual(arp.pdst, dip)
2619
2620     def test_vpws(self):
2621         """Virtual Private Wire Service"""
2622
2623         #
2624         # Create an MPLS tunnel that pushes 1 label
2625         # For Ethernet over MPLS the uniform mode is irrelevant since ttl/cos
2626         # information is not in the packet, but we test it works anyway
2627         #
2628         mpls_tun_1 = VppMPLSTunnelInterface(
2629             self,
2630             [
2631                 VppRoutePath(
2632                     self.pg0.remote_ip4,
2633                     self.pg0.sw_if_index,
2634                     labels=[VppMplsLabel(42, MplsLspMode.UNIFORM)],
2635                 )
2636             ],
2637             is_l2=1,
2638         )
2639         mpls_tun_1.add_vpp_config()
2640         mpls_tun_1.admin_up()
2641
2642         #
2643         # Create a label entry to for 55 that does L2 input to the tunnel
2644         #
2645         route_55_eos = VppMplsRoute(
2646             self,
2647             55,
2648             1,
2649             [
2650                 VppRoutePath(
2651                     "0.0.0.0",
2652                     mpls_tun_1.sw_if_index,
2653                     type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2654                     proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET,
2655                 )
2656             ],
2657             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET,
2658         )
2659         route_55_eos.add_vpp_config()
2660
2661         #
2662         # Cross-connect the tunnel with one of the customers L2 interfaces
2663         #
2664         self.vapi.sw_interface_set_l2_xconnect(
2665             self.pg1.sw_if_index, mpls_tun_1.sw_if_index, enable=1
2666         )
2667         self.vapi.sw_interface_set_l2_xconnect(
2668             mpls_tun_1.sw_if_index, self.pg1.sw_if_index, enable=1
2669         )
2670
2671         #
2672         # inject a packet from the core
2673         #
2674         pcore = (
2675             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2676             / MPLS(label=55, ttl=64)
2677             / Ether(dst="00:00:de:ad:ba:be", src="00:00:de:ad:be:ef")
2678             / IP(src="10.10.10.10", dst="11.11.11.11")
2679             / UDP(sport=1234, dport=1234)
2680             / Raw(b"\xa5" * 100)
2681         )
2682
2683         tx0 = pcore * NUM_PKTS
2684         rx0 = self.send_and_expect(self.pg0, tx0, self.pg1)
2685         payload = pcore[MPLS].payload
2686
2687         self.assertEqual(rx0[0][Ether].dst, payload[Ether].dst)
2688         self.assertEqual(rx0[0][Ether].src, payload[Ether].src)
2689
2690         #
2691         # Inject a packet from the customer/L2 side
2692         # there's no resolved ARP entry so the first packet we see should be
2693         # an ARP request
2694         #
2695         tx1 = pcore[MPLS].payload
2696         rx1 = self.send_and_expect(self.pg1, [tx1], self.pg0)
2697
2698         self.verify_arp_req(
2699             rx1[0], self.pg0.local_mac, self.pg0.local_ip4, self.pg0.remote_ip4
2700         )
2701
2702         #
2703         # resolve the ARP entries and send again
2704         #
2705         self.pg0.resolve_arp()
2706         tx1 = pcore[MPLS].payload * NUM_PKTS
2707         rx1 = self.send_and_expect(self.pg1, tx1, self.pg0)
2708
2709         self.verify_capture_tunneled_ethernet(rx1, tx1, [VppMplsLabel(42)])
2710
2711     def test_vpls(self):
2712         """Virtual Private LAN Service"""
2713
2714         # we skipped this in the setup
2715         self.pg0.resolve_arp()
2716
2717         #
2718         # Create a L2 MPLS tunnels
2719         #
2720         mpls_tun1 = VppMPLSTunnelInterface(
2721             self,
2722             [
2723                 VppRoutePath(
2724                     self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[VppMplsLabel(42)]
2725                 )
2726             ],
2727             is_l2=1,
2728         )
2729         mpls_tun1.add_vpp_config()
2730         mpls_tun1.admin_up()
2731
2732         mpls_tun2 = VppMPLSTunnelInterface(
2733             self,
2734             [
2735                 VppRoutePath(
2736                     self.pg0.remote_ip4, self.pg0.sw_if_index, labels=[VppMplsLabel(43)]
2737                 )
2738             ],
2739             is_l2=1,
2740         )
2741         mpls_tun2.add_vpp_config()
2742         mpls_tun2.admin_up()
2743
2744         #
2745         # Create a label entries, 55 and 56, that do L2 input to the tunnel
2746         # the latter includes a Psuedo Wire Control Word
2747         #
2748         route_55_eos = VppMplsRoute(
2749             self,
2750             55,
2751             1,
2752             [
2753                 VppRoutePath(
2754                     "0.0.0.0",
2755                     mpls_tun1.sw_if_index,
2756                     type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2757                     proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET,
2758                 )
2759             ],
2760             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET,
2761         )
2762
2763         route_56_eos = VppMplsRoute(
2764             self,
2765             56,
2766             1,
2767             [
2768                 VppRoutePath(
2769                     "0.0.0.0",
2770                     mpls_tun2.sw_if_index,
2771                     type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2772                     flags=FibPathFlags.FIB_PATH_FLAG_POP_PW_CW,
2773                     proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET,
2774                 )
2775             ],
2776             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET,
2777         )
2778
2779         # move me
2780         route_56_eos.add_vpp_config()
2781         route_55_eos.add_vpp_config()
2782
2783         self.logger.info(self.vapi.cli("sh mpls fib 56"))
2784
2785         #
2786         # add to tunnel to the customers bridge-domain
2787         #
2788         self.vapi.sw_interface_set_l2_bridge(
2789             rx_sw_if_index=mpls_tun1.sw_if_index, bd_id=1
2790         )
2791         self.vapi.sw_interface_set_l2_bridge(
2792             rx_sw_if_index=mpls_tun2.sw_if_index, bd_id=1
2793         )
2794         self.vapi.sw_interface_set_l2_bridge(
2795             rx_sw_if_index=self.pg1.sw_if_index, bd_id=1
2796         )
2797
2798         #
2799         # Packet from host on the customer interface to each host
2800         # reachable over the core, and vice-versa
2801         #
2802         p_cust1 = (
2803             Ether(dst="00:00:de:ad:ba:b1", src="00:00:de:ad:be:ef")
2804             / IP(src="10.10.10.10", dst="11.11.11.11")
2805             / UDP(sport=1234, dport=1234)
2806             / Raw(b"\xa5" * 100)
2807         )
2808         p_cust2 = (
2809             Ether(dst="00:00:de:ad:ba:b2", src="00:00:de:ad:be:ef")
2810             / IP(src="10.10.10.10", dst="11.11.11.12")
2811             / UDP(sport=1234, dport=1234)
2812             / Raw(b"\xa5" * 100)
2813         )
2814         p_core1 = (
2815             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2816             / MPLS(label=55, ttl=64)
2817             / Ether(src="00:00:de:ad:ba:b1", dst="00:00:de:ad:be:ef")
2818             / IP(dst="10.10.10.10", src="11.11.11.11")
2819             / UDP(sport=1234, dport=1234)
2820             / Raw(b"\xa5" * 100)
2821         )
2822         p_core2 = (
2823             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2824             / MPLS(label=56, ttl=64)
2825             / Raw(b"\x01" * 4)
2826             / Ether(src="00:00:de:ad:ba:b2", dst="00:00:de:ad:be:ef")  # PW CW
2827             / IP(dst="10.10.10.10", src="11.11.11.12")
2828             / UDP(sport=1234, dport=1234)
2829             / Raw(b"\xa5" * 100)
2830         )
2831
2832         #
2833         # The BD is learning, so send in one of each packet to learn
2834         #
2835
2836         # 2 packets due to BD flooding
2837         rx = self.send_and_expect(self.pg1, p_cust1, self.pg0, n_rx=2)
2838         rx = self.send_and_expect(self.pg1, p_cust2, self.pg0, n_rx=2)
2839
2840         # we've learnt this so expect it be be forwarded not flooded
2841         rx = self.send_and_expect(self.pg0, [p_core1], self.pg1)
2842         self.assertEqual(rx[0][Ether].dst, p_cust1[Ether].src)
2843         self.assertEqual(rx[0][Ether].src, p_cust1[Ether].dst)
2844
2845         rx = self.send_and_expect(self.pg0, [p_core2], self.pg1)
2846         self.assertEqual(rx[0][Ether].dst, p_cust2[Ether].src)
2847         self.assertEqual(rx[0][Ether].src, p_cust2[Ether].dst)
2848
2849         #
2850         # now a stream in each direction from each host
2851         #
2852         rx = self.send_and_expect(self.pg1, p_cust1 * NUM_PKTS, self.pg0)
2853         self.verify_capture_tunneled_ethernet(
2854             rx, p_cust1 * NUM_PKTS, [VppMplsLabel(42)]
2855         )
2856
2857         rx = self.send_and_expect(self.pg1, p_cust2 * NUM_PKTS, self.pg0)
2858         self.verify_capture_tunneled_ethernet(
2859             rx, p_cust2 * NUM_PKTS, [VppMplsLabel(43)]
2860         )
2861
2862         rx = self.send_and_expect(self.pg0, p_core1 * NUM_PKTS, self.pg1)
2863         rx = self.send_and_expect(self.pg0, p_core2 * NUM_PKTS, self.pg1)
2864
2865         #
2866         # remove interfaces from customers bridge-domain
2867         #
2868         self.vapi.sw_interface_set_l2_bridge(
2869             rx_sw_if_index=mpls_tun1.sw_if_index, bd_id=1, enable=0
2870         )
2871         self.vapi.sw_interface_set_l2_bridge(
2872             rx_sw_if_index=mpls_tun2.sw_if_index, bd_id=1, enable=0
2873         )
2874         self.vapi.sw_interface_set_l2_bridge(
2875             rx_sw_if_index=self.pg1.sw_if_index, bd_id=1, enable=0
2876         )
2877
2878
2879 if __name__ == "__main__":
2880     unittest.main(testRunner=VppTestRunner)