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