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