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