vppinfra: toeplitz hash
[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(100):
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         for ii in range(100):
1082             mpls_tun = VppMPLSTunnelInterface(
1083                 self,
1084                 [VppRoutePath(self.pg0.remote_ip4,
1085                               self.pg0.sw_if_index,
1086                               labels=[VppMplsLabel(44, ttl=32),
1087                                       VppMplsLabel(46, MplsLspMode.UNIFORM)])],
1088                 is_l2=1)
1089             mpls_tun.add_vpp_config()
1090             mpls_tun.admin_up()
1091
1092     def test_v4_exp_null(self):
1093         """ MPLS V4 Explicit NULL test """
1094
1095         #
1096         # The first test case has an MPLS TTL of 0
1097         # all packet should be dropped
1098         #
1099         tx = self.create_stream_labelled_ip4(self.pg0,
1100                                              [VppMplsLabel(0, ttl=0)])
1101         self.send_and_assert_no_replies(self.pg0, tx,
1102                                         "MPLS TTL=0 packets forwarded")
1103
1104         #
1105         # a stream with a non-zero MPLS TTL
1106         # PG0 is in the default table
1107         #
1108         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(0)])
1109         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1110         self.verify_capture_ip4(self.pg0, rx, tx)
1111
1112         #
1113         # a stream with a non-zero MPLS TTL
1114         # PG1 is in table 1
1115         # we are ensuring the post-pop lookup occurs in the VRF table
1116         #
1117         tx = self.create_stream_labelled_ip4(self.pg1, [VppMplsLabel(0)])
1118         rx = self.send_and_expect(self.pg1, tx, self.pg1)
1119         self.verify_capture_ip4(self.pg1, rx, tx)
1120
1121     def test_v6_exp_null(self):
1122         """ MPLS V6 Explicit NULL test """
1123
1124         #
1125         # a stream with a non-zero MPLS TTL
1126         # PG0 is in the default table
1127         #
1128         tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(2)])
1129         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1130         self.verify_capture_ip6(self.pg0, rx, tx)
1131
1132         #
1133         # a stream with a non-zero MPLS TTL
1134         # PG1 is in table 1
1135         # we are ensuring the post-pop lookup occurs in the VRF table
1136         #
1137         tx = self.create_stream_labelled_ip6(self.pg1, [VppMplsLabel(2)])
1138         rx = self.send_and_expect(self.pg1, tx, self.pg1)
1139         self.verify_capture_ip6(self.pg0, rx, tx)
1140
1141     def test_deag(self):
1142         """ MPLS Deagg """
1143
1144         #
1145         # A de-agg route - next-hop lookup in default table
1146         #
1147         route_34_eos = VppMplsRoute(self, 34, 1,
1148                                     [VppRoutePath("0.0.0.0",
1149                                                   0xffffffff,
1150                                                   nh_table_id=0)])
1151         route_34_eos.add_vpp_config()
1152
1153         #
1154         # ping an interface in the default table
1155         # PG0 is in the default table
1156         #
1157         tx = self.create_stream_labelled_ip4(self.pg0,
1158                                              [VppMplsLabel(34)],
1159                                              ping=1,
1160                                              ip_itf=self.pg0)
1161         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1162         self.verify_capture_ip4(self.pg0, rx, tx, ping_resp=1)
1163
1164         #
1165         # A de-agg route - next-hop lookup in non-default table
1166         #
1167         route_35_eos = VppMplsRoute(self, 35, 1,
1168                                     [VppRoutePath("0.0.0.0",
1169                                                   0xffffffff,
1170                                                   nh_table_id=1)])
1171         route_35_eos.add_vpp_config()
1172
1173         #
1174         # ping an interface in the non-default table
1175         # PG0 is in the default table. packet arrive labelled in the
1176         # default table and egress unlabelled in the non-default
1177         #
1178         tx = self.create_stream_labelled_ip4(
1179             self.pg0, [VppMplsLabel(35)], ping=1, ip_itf=self.pg1)
1180         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1181         self.verify_capture_ip4(self.pg1, rx, tx, ping_resp=1)
1182
1183         #
1184         # Double pop
1185         #
1186         route_36_neos = VppMplsRoute(self, 36, 0,
1187                                      [VppRoutePath("0.0.0.0",
1188                                                    0xffffffff)])
1189         route_36_neos.add_vpp_config()
1190
1191         tx = self.create_stream_labelled_ip4(self.pg0,
1192                                              [VppMplsLabel(36),
1193                                               VppMplsLabel(35)],
1194                                              ping=1, ip_itf=self.pg1)
1195         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1196         self.verify_capture_ip4(self.pg1, rx, tx, ping_resp=1)
1197
1198         route_36_neos.remove_vpp_config()
1199         route_35_eos.remove_vpp_config()
1200         route_34_eos.remove_vpp_config()
1201
1202     def test_interface_rx(self):
1203         """ MPLS Interface Receive """
1204
1205         #
1206         # Add a non-recursive route that will forward the traffic
1207         # post-interface-rx
1208         #
1209         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
1210                                     table_id=1,
1211                                     paths=[VppRoutePath(self.pg1.remote_ip4,
1212                                                         self.pg1.sw_if_index)])
1213         route_10_0_0_1.add_vpp_config()
1214
1215         #
1216         # An interface receive label that maps traffic to RX on interface
1217         # pg1
1218         # by injecting the packet in on pg0, which is in table 0
1219         # doing an interface-rx on pg1 and matching a route in table 1
1220         # if the packet egresses, then we must have swapped to pg1
1221         # so as to have matched the route in table 1
1222         #
1223         route_34_eos = VppMplsRoute(
1224             self, 34, 1,
1225             [VppRoutePath("0.0.0.0",
1226                           self.pg1.sw_if_index,
1227                           type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX)])
1228         route_34_eos.add_vpp_config()
1229
1230         #
1231         # ping an interface in the default table
1232         # PG0 is in the default table
1233         #
1234         tx = self.create_stream_labelled_ip4(self.pg0,
1235                                              [VppMplsLabel(34)],
1236                                              dst_ip="10.0.0.1")
1237         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1238         self.verify_capture_ip4(self.pg1, rx, tx)
1239
1240     def test_mcast_mid_point(self):
1241         """ MPLS Multicast Mid Point """
1242
1243         #
1244         # Add a non-recursive route that will forward the traffic
1245         # post-interface-rx
1246         #
1247         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
1248                                     table_id=1,
1249                                     paths=[VppRoutePath(self.pg1.remote_ip4,
1250                                                         self.pg1.sw_if_index)])
1251         route_10_0_0_1.add_vpp_config()
1252
1253         #
1254         # Add a mcast entry that replicate to pg2 and pg3
1255         # and replicate to a interface-rx (like a bud node would)
1256         #
1257         route_3400_eos = VppMplsRoute(
1258             self, 3400, 1,
1259             [VppRoutePath(self.pg2.remote_ip4,
1260                           self.pg2.sw_if_index,
1261                           labels=[VppMplsLabel(3401)]),
1262              VppRoutePath(self.pg3.remote_ip4,
1263                           self.pg3.sw_if_index,
1264                           labels=[VppMplsLabel(3402)]),
1265              VppRoutePath("0.0.0.0",
1266                           self.pg1.sw_if_index,
1267                           type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX)],
1268             is_multicast=1)
1269         route_3400_eos.add_vpp_config()
1270
1271         #
1272         # ping an interface in the default table
1273         # PG0 is in the default table
1274         #
1275         self.vapi.cli("clear trace")
1276         tx = self.create_stream_labelled_ip4(self.pg0,
1277                                              [VppMplsLabel(3400, ttl=64)],
1278                                              n=257,
1279                                              dst_ip="10.0.0.1")
1280         self.pg0.add_stream(tx)
1281
1282         self.pg_enable_capture(self.pg_interfaces)
1283         self.pg_start()
1284
1285         rx = self.pg1.get_capture(257)
1286         self.verify_capture_ip4(self.pg1, rx, tx)
1287
1288         rx = self.pg2.get_capture(257)
1289         self.verify_capture_labelled(self.pg2, rx, tx,
1290                                      [VppMplsLabel(3401, ttl=63)])
1291         rx = self.pg3.get_capture(257)
1292         self.verify_capture_labelled(self.pg3, rx, tx,
1293                                      [VppMplsLabel(3402, ttl=63)])
1294
1295     def test_mcast_head(self):
1296         """ MPLS Multicast Head-end """
1297
1298         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
1299         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
1300
1301         #
1302         # Create a multicast tunnel with two replications
1303         #
1304         mpls_tun = VppMPLSTunnelInterface(
1305             self,
1306             [VppRoutePath(self.pg2.remote_ip4,
1307                           self.pg2.sw_if_index,
1308                           labels=[VppMplsLabel(42)]),
1309              VppRoutePath(self.pg3.remote_ip4,
1310                           self.pg3.sw_if_index,
1311                           labels=[VppMplsLabel(43)])],
1312             is_multicast=1)
1313         mpls_tun.add_vpp_config()
1314         mpls_tun.admin_up()
1315
1316         #
1317         # add an unlabelled route through the new tunnel
1318         #
1319         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
1320                                     [VppRoutePath("0.0.0.0",
1321                                                   mpls_tun._sw_if_index)])
1322         route_10_0_0_3.add_vpp_config()
1323
1324         self.vapi.cli("clear trace")
1325         tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
1326         self.pg0.add_stream(tx)
1327
1328         self.pg_enable_capture(self.pg_interfaces)
1329         self.pg_start()
1330
1331         rx = self.pg2.get_capture(257)
1332         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(42)])
1333         rx = self.pg3.get_capture(257)
1334         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(43)])
1335
1336         #
1337         # An an IP multicast route via the tunnel
1338         # A (*,G).
1339         # one accepting interface, pg0, 1 forwarding interface via the tunnel
1340         #
1341         route_232_1_1_1 = VppIpMRoute(
1342             self,
1343             "0.0.0.0",
1344             "232.1.1.1", 32,
1345             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
1346             [VppMRoutePath(self.pg0.sw_if_index,
1347                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
1348              VppMRoutePath(mpls_tun._sw_if_index,
1349                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
1350         route_232_1_1_1.add_vpp_config()
1351         self.logger.info(self.vapi.cli("sh ip mfib index 0"))
1352
1353         self.vapi.cli("clear trace")
1354         tx = self.create_stream_ip4(self.pg0, "232.1.1.1")
1355         self.pg0.add_stream(tx)
1356
1357         self.pg_enable_capture(self.pg_interfaces)
1358         self.pg_start()
1359
1360         rx = self.pg2.get_capture(257)
1361         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(42)])
1362         rx = self.pg3.get_capture(257)
1363         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(43)])
1364
1365     def test_mcast_ip4_tail(self):
1366         """ MPLS IPv4 Multicast Tail """
1367
1368         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
1369         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
1370
1371         #
1372         # Add a multicast route that will forward the traffic
1373         # post-disposition
1374         #
1375         route_232_1_1_1 = VppIpMRoute(
1376             self,
1377             "0.0.0.0",
1378             "232.1.1.1", 32,
1379             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
1380             table_id=1,
1381             paths=[VppMRoutePath(self.pg1.sw_if_index,
1382                                  MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
1383         route_232_1_1_1.add_vpp_config()
1384
1385         #
1386         # An interface receive label that maps traffic to RX on interface
1387         # pg1
1388         # by injecting the packet in on pg0, which is in table 0
1389         # doing an rpf-id  and matching a route in table 1
1390         # if the packet egresses, then we must have matched the route in
1391         # table 1
1392         #
1393         route_34_eos = VppMplsRoute(
1394             self, 34, 1,
1395             [VppRoutePath("0.0.0.0",
1396                           0xffffffff,
1397                           nh_table_id=1,
1398                           rpf_id=55)],
1399             is_multicast=1,
1400             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP4)
1401
1402         route_34_eos.add_vpp_config()
1403
1404         #
1405         # Drop due to interface lookup miss
1406         #
1407         self.vapi.cli("clear trace")
1408         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(34)],
1409                                              dst_ip="232.1.1.1", n=1)
1410         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop none")
1411
1412         #
1413         # set the RPF-ID of the entry to match the input packet's
1414         #
1415         route_232_1_1_1.update_rpf_id(55)
1416         self.logger.info(self.vapi.cli("sh ip mfib index 1 232.1.1.1"))
1417
1418         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(34)],
1419                                              dst_ip="232.1.1.1")
1420         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1421         self.verify_capture_ip4(self.pg1, rx, tx)
1422
1423         #
1424         # disposed packets have an invalid IPv4 checksum
1425         #
1426         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(34)],
1427                                              dst_ip="232.1.1.1", n=65,
1428                                              chksum=1)
1429         self.send_and_assert_no_replies(self.pg0, tx, "Invalid Checksum")
1430
1431         #
1432         # set the RPF-ID of the entry to not match the input packet's
1433         #
1434         route_232_1_1_1.update_rpf_id(56)
1435         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(34)],
1436                                              dst_ip="232.1.1.1")
1437         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
1438
1439     def test_mcast_ip6_tail(self):
1440         """ MPLS IPv6 Multicast Tail """
1441
1442         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
1443         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
1444
1445         #
1446         # Add a multicast route that will forward the traffic
1447         # post-disposition
1448         #
1449         route_ff = VppIpMRoute(
1450             self,
1451             "::",
1452             "ff01::1", 32,
1453             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
1454             table_id=1,
1455             paths=[VppMRoutePath(self.pg1.sw_if_index,
1456                                  MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
1457                                  proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)])
1458         route_ff.add_vpp_config()
1459
1460         #
1461         # An interface receive label that maps traffic to RX on interface
1462         # pg1
1463         # by injecting the packet in on pg0, which is in table 0
1464         # doing an rpf-id  and matching a route in table 1
1465         # if the packet egresses, then we must have matched the route in
1466         # table 1
1467         #
1468         route_34_eos = VppMplsRoute(
1469             self, 34, 1,
1470             [VppRoutePath("::",
1471                           0xffffffff,
1472                           nh_table_id=1,
1473                           rpf_id=55)],
1474             is_multicast=1,
1475             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)
1476
1477         route_34_eos.add_vpp_config()
1478
1479         #
1480         # Drop due to interface lookup miss
1481         #
1482         tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(34)],
1483                                              dst_ip="ff01::1")
1484         self.send_and_assert_no_replies(self.pg0, tx, "RPF Miss")
1485
1486         #
1487         # set the RPF-ID of the entry to match the input packet's
1488         #
1489         route_ff.update_rpf_id(55)
1490
1491         tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(34)],
1492                                              dst_ip="ff01::1")
1493         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1494         self.verify_capture_ip6(self.pg1, rx, tx)
1495
1496         #
1497         # disposed packets have hop-limit = 1
1498         #
1499         tx = self.create_stream_labelled_ip6(self.pg0,
1500                                              [VppMplsLabel(34)],
1501                                              dst_ip="ff01::1",
1502                                              hlim=1)
1503         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1504         self.verify_capture_ip6_icmp(self.pg0, rx, tx)
1505
1506         #
1507         # set the RPF-ID of the entry to not match the input packet's
1508         #
1509         route_ff.update_rpf_id(56)
1510         tx = self.create_stream_labelled_ip6(self.pg0,
1511                                              [VppMplsLabel(34)],
1512                                              dst_ip="ff01::1")
1513         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
1514
1515     def test_6pe(self):
1516         """ MPLS 6PE """
1517
1518         #
1519         # Add a non-recursive route with a single out label
1520         #
1521         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
1522                                     [VppRoutePath(self.pg0.remote_ip4,
1523                                                   self.pg0.sw_if_index,
1524                                                   labels=[VppMplsLabel(45)])])
1525         route_10_0_0_1.add_vpp_config()
1526
1527         # bind a local label to the route
1528         binding = VppMplsIpBind(self, 44, "10.0.0.1", 32)
1529         binding.add_vpp_config()
1530
1531         #
1532         # a labelled v6 route that resolves through the v4
1533         #
1534         route_2001_3 = VppIpRoute(
1535             self, "2001::3", 128,
1536             [VppRoutePath("10.0.0.1",
1537                           INVALID_INDEX,
1538                           labels=[VppMplsLabel(32)])])
1539         route_2001_3.add_vpp_config()
1540
1541         tx = self.create_stream_ip6(self.pg0, "2001::3")
1542         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1543
1544         self.verify_capture_labelled_ip6(self.pg0, rx, tx,
1545                                          [VppMplsLabel(45),
1546                                           VppMplsLabel(32)])
1547
1548         #
1549         # and a v4 recursive via the v6
1550         #
1551         route_20_3 = VppIpRoute(
1552             self, "20.0.0.3", 32,
1553             [VppRoutePath("2001::3",
1554                           INVALID_INDEX,
1555                           labels=[VppMplsLabel(99)])])
1556         route_20_3.add_vpp_config()
1557
1558         tx = self.create_stream_ip4(self.pg0, "20.0.0.3")
1559         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1560
1561         self.verify_capture_labelled_ip4(self.pg0, rx, tx,
1562                                          [VppMplsLabel(45),
1563                                           VppMplsLabel(32),
1564                                           VppMplsLabel(99)])
1565
1566
1567 class TestMPLSDisabled(VppTestCase):
1568     """ MPLS disabled """
1569
1570     @classmethod
1571     def setUpClass(cls):
1572         super(TestMPLSDisabled, cls).setUpClass()
1573
1574     @classmethod
1575     def tearDownClass(cls):
1576         super(TestMPLSDisabled, cls).tearDownClass()
1577
1578     def setUp(self):
1579         super(TestMPLSDisabled, self).setUp()
1580
1581         # create 2 pg interfaces
1582         self.create_pg_interfaces(range(2))
1583
1584         self.tbl = VppMplsTable(self, 0)
1585         self.tbl.add_vpp_config()
1586
1587         # PG0 is MPLS enabled
1588         self.pg0.admin_up()
1589         self.pg0.config_ip4()
1590         self.pg0.resolve_arp()
1591         self.pg0.enable_mpls()
1592
1593         # PG 1 is not MPLS enabled
1594         self.pg1.admin_up()
1595
1596     def tearDown(self):
1597         for i in self.pg_interfaces:
1598             i.unconfig_ip4()
1599             i.admin_down()
1600
1601         self.pg0.disable_mpls()
1602         super(TestMPLSDisabled, self).tearDown()
1603
1604     def test_mpls_disabled(self):
1605         """ MPLS Disabled """
1606
1607         self.logger.info(self.vapi.cli("show mpls interface"))
1608         self.logger.info(self.vapi.cli("show mpls interface pg1"))
1609         self.logger.info(self.vapi.cli("show mpls interface pg0"))
1610
1611         tx = (Ether(src=self.pg1.remote_mac,
1612                     dst=self.pg1.local_mac) /
1613               MPLS(label=32, ttl=64) /
1614               IPv6(src="2001::1", dst=self.pg0.remote_ip6) /
1615               UDP(sport=1234, dport=1234) /
1616               Raw(b'\xa5' * 100))
1617
1618         #
1619         # A simple MPLS xconnect - eos label in label out
1620         #
1621         route_32_eos = VppMplsRoute(self, 32, 1,
1622                                     [VppRoutePath(self.pg0.remote_ip4,
1623                                                   self.pg0.sw_if_index,
1624                                                   labels=[33])])
1625         route_32_eos.add_vpp_config()
1626
1627         #
1628         # PG1 does not forward IP traffic
1629         #
1630         self.send_and_assert_no_replies(self.pg1, tx, "MPLS disabled")
1631
1632         #
1633         # MPLS enable PG1
1634         #
1635         self.pg1.enable_mpls()
1636
1637         self.logger.info(self.vapi.cli("show mpls interface"))
1638         self.logger.info(self.vapi.cli("show mpls interface pg1"))
1639
1640         #
1641         # Now we get packets through
1642         #
1643         self.pg1.add_stream(tx)
1644         self.pg_enable_capture(self.pg_interfaces)
1645         self.pg_start()
1646
1647         rx = self.pg0.get_capture(1)
1648
1649         #
1650         # Disable PG1
1651         #
1652         self.pg1.disable_mpls()
1653
1654         #
1655         # PG1 does not forward IP traffic
1656         #
1657         self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1658         self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1659
1660
1661 class TestMPLSPIC(VppTestCase):
1662     """ MPLS Prefix-Independent Convergence (PIC) edge convergence """
1663
1664     @classmethod
1665     def setUpClass(cls):
1666         super(TestMPLSPIC, cls).setUpClass()
1667
1668     @classmethod
1669     def tearDownClass(cls):
1670         super(TestMPLSPIC, cls).tearDownClass()
1671
1672     def setUp(self):
1673         super(TestMPLSPIC, self).setUp()
1674
1675         # create 2 pg interfaces
1676         self.create_pg_interfaces(range(4))
1677
1678         mpls_tbl = VppMplsTable(self, 0)
1679         mpls_tbl.add_vpp_config()
1680         tbl4 = VppIpTable(self, 1)
1681         tbl4.add_vpp_config()
1682         tbl6 = VppIpTable(self, 1, is_ip6=1)
1683         tbl6.add_vpp_config()
1684
1685         # core links
1686         self.pg0.admin_up()
1687         self.pg0.config_ip4()
1688         self.pg0.resolve_arp()
1689         self.pg0.enable_mpls()
1690
1691         self.pg1.admin_up()
1692         self.pg1.config_ip4()
1693         self.pg1.resolve_arp()
1694         self.pg1.enable_mpls()
1695
1696         # VRF (customer facing) link
1697         self.pg2.admin_up()
1698         self.pg2.set_table_ip4(1)
1699         self.pg2.config_ip4()
1700         self.pg2.resolve_arp()
1701         self.pg2.set_table_ip6(1)
1702         self.pg2.config_ip6()
1703         self.pg2.resolve_ndp()
1704
1705         self.pg3.admin_up()
1706         self.pg3.set_table_ip4(1)
1707         self.pg3.config_ip4()
1708         self.pg3.resolve_arp()
1709         self.pg3.set_table_ip6(1)
1710         self.pg3.config_ip6()
1711         self.pg3.resolve_ndp()
1712
1713     def tearDown(self):
1714         self.pg0.disable_mpls()
1715         self.pg1.disable_mpls()
1716         for i in self.pg_interfaces:
1717             i.unconfig_ip4()
1718             i.unconfig_ip6()
1719             i.set_table_ip4(0)
1720             i.set_table_ip6(0)
1721             i.admin_down()
1722         super(TestMPLSPIC, self).tearDown()
1723
1724     def test_mpls_ibgp_pic(self):
1725         """ MPLS iBGP Prefix-Independent Convergence (PIC) edge convergence
1726
1727         1) setup many iBGP VPN routes via a pair of iBGP peers.
1728         2) Check EMCP forwarding to these peers
1729         3) withdraw the IGP route to one of these peers.
1730         4) check forwarding continues to the remaining peer
1731         """
1732
1733         #
1734         # IGP+LDP core routes
1735         #
1736         core_10_0_0_45 = VppIpRoute(self, "10.0.0.45", 32,
1737                                     [VppRoutePath(self.pg0.remote_ip4,
1738                                                   self.pg0.sw_if_index,
1739                                                   labels=[45])])
1740         core_10_0_0_45.add_vpp_config()
1741
1742         core_10_0_0_46 = VppIpRoute(self, "10.0.0.46", 32,
1743                                     [VppRoutePath(self.pg1.remote_ip4,
1744                                                   self.pg1.sw_if_index,
1745                                                   labels=[46])])
1746         core_10_0_0_46.add_vpp_config()
1747
1748         #
1749         # Lot's of VPN routes. We need more the 64 so VPP will build
1750         # the fast convergence indirection
1751         #
1752         vpn_routes = []
1753         pkts = []
1754         for ii in range(NUM_PKTS):
1755             dst = "192.168.1.%d" % ii
1756             vpn_routes.append(VppIpRoute(
1757                 self, dst, 32,
1758                 [VppRoutePath(
1759                     "10.0.0.45",
1760                     0xffffffff,
1761                     labels=[145],
1762                     flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_HOST),
1763                  VppRoutePath(
1764                      "10.0.0.46",
1765                      0xffffffff,
1766                      labels=[146],
1767                      flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_HOST)],
1768                 table_id=1))
1769             vpn_routes[ii].add_vpp_config()
1770
1771             pkts.append(Ether(dst=self.pg2.local_mac,
1772                               src=self.pg2.remote_mac) /
1773                         IP(src=self.pg2.remote_ip4, dst=dst) /
1774                         UDP(sport=1234, dport=1234) /
1775                         Raw(b'\xa5' * 100))
1776
1777         #
1778         # Send the packet stream (one pkt to each VPN route)
1779         #  - expect a 50-50 split of the traffic
1780         #
1781         self.pg2.add_stream(pkts)
1782         self.pg_enable_capture(self.pg_interfaces)
1783         self.pg_start()
1784
1785         rx0 = self.pg0._get_capture(NUM_PKTS)
1786         rx1 = self.pg1._get_capture(NUM_PKTS)
1787
1788         # not testing the LB hashing algorithm so we're not concerned
1789         # with the split ratio, just as long as neither is 0
1790         self.assertNotEqual(0, len(rx0))
1791         self.assertNotEqual(0, len(rx1))
1792         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1793                          "Expected all (%s) packets across both ECMP paths. "
1794                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1795
1796         #
1797         # use a test CLI command to stop the FIB walk process, this
1798         # will prevent the FIB converging the VPN routes and thus allow
1799         # us to probe the interim (post-fail, pre-converge) state
1800         #
1801         self.vapi.ppcli("test fib-walk-process disable")
1802
1803         #
1804         # Withdraw one of the IGP routes
1805         #
1806         core_10_0_0_46.remove_vpp_config()
1807
1808         #
1809         # now all packets should be forwarded through the remaining peer
1810         #
1811         self.vapi.ppcli("clear trace")
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         # enable the FIB walk process to converge the FIB
1823         #
1824         self.vapi.ppcli("test fib-walk-process enable")
1825
1826         #
1827         # packets should still be forwarded through the remaining peer
1828         #
1829         self.pg2.add_stream(pkts)
1830         self.pg_enable_capture(self.pg_interfaces)
1831         self.pg_start()
1832
1833         rx0 = self.pg0.get_capture(NUM_PKTS)
1834         self.assertEqual(len(pkts), len(rx0),
1835                          "Expected all (%s) packets across single path. "
1836                          "rx0: %s." % (len(pkts), len(rx0)))
1837
1838         #
1839         # Add the IGP route back and we return to load-balancing
1840         #
1841         core_10_0_0_46.add_vpp_config()
1842
1843         self.pg2.add_stream(pkts)
1844         self.pg_enable_capture(self.pg_interfaces)
1845         self.pg_start()
1846
1847         rx0 = self.pg0._get_capture(NUM_PKTS)
1848         rx1 = self.pg1._get_capture(NUM_PKTS)
1849         self.assertNotEqual(0, len(rx0))
1850         self.assertNotEqual(0, len(rx1))
1851         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1852                          "Expected all (%s) packets across both ECMP paths. "
1853                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1854
1855     def test_mpls_ebgp_pic(self):
1856         """ MPLS eBGP Prefix-Independent Convergence (PIC) edge convergence
1857
1858         1) setup many eBGP VPN routes via a pair of eBGP peers.
1859         2) Check EMCP forwarding to these peers
1860         3) withdraw one eBGP path - expect LB across remaining eBGP
1861         """
1862
1863         #
1864         # Lot's of VPN routes. We need more the 64 so VPP will build
1865         # the fast convergence indirection
1866         #
1867         vpn_routes = []
1868         vpn_bindings = []
1869         pkts = []
1870         for ii in range(NUM_PKTS):
1871             dst = "192.168.1.%d" % ii
1872             local_label = 1600 + ii
1873             vpn_routes.append(VppIpRoute(
1874                 self, dst, 32,
1875                 [VppRoutePath(
1876                     self.pg2.remote_ip4,
1877                     0xffffffff,
1878                     nh_table_id=1,
1879                     flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED),
1880                  VppRoutePath(
1881                      self.pg3.remote_ip4,
1882                      0xffffffff,
1883                      nh_table_id=1,
1884                      flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED)],
1885                 table_id=1))
1886             vpn_routes[ii].add_vpp_config()
1887
1888             vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 32,
1889                                               ip_table_id=1))
1890             vpn_bindings[ii].add_vpp_config()
1891
1892             pkts.append(Ether(dst=self.pg0.local_mac,
1893                               src=self.pg0.remote_mac) /
1894                         MPLS(label=local_label, ttl=64) /
1895                         IP(src=self.pg0.remote_ip4, dst=dst) /
1896                         UDP(sport=1234, dport=1234) /
1897                         Raw(b'\xa5' * 100))
1898
1899         #
1900         # Send the packet stream (one pkt to each VPN route)
1901         #  - expect a 50-50 split of the traffic
1902         #
1903         self.pg0.add_stream(pkts)
1904         self.pg_enable_capture(self.pg_interfaces)
1905         self.pg_start()
1906
1907         rx0 = self.pg2._get_capture(NUM_PKTS)
1908         rx1 = self.pg3._get_capture(NUM_PKTS)
1909
1910         # not testing the LB hashing algorithm so we're not concerned
1911         # with the split ratio, just as long as neither is 0
1912         self.assertNotEqual(0, len(rx0))
1913         self.assertNotEqual(0, len(rx1))
1914         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1915                          "Expected all (%s) packets across both ECMP paths. "
1916                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1917
1918         #
1919         # use a test CLI command to stop the FIB walk process, this
1920         # will prevent the FIB converging the VPN routes and thus allow
1921         # us to probe the interim (post-fail, pre-converge) state
1922         #
1923         self.vapi.ppcli("test fib-walk-process disable")
1924
1925         #
1926         # withdraw the connected prefix on the interface.
1927         #
1928         self.pg2.unconfig_ip4()
1929
1930         #
1931         # now all packets should 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         # enable the FIB walk process to converge the FIB
1944         #
1945         self.vapi.ppcli("test fib-walk-process enable")
1946
1947         #
1948         # packets should still be forwarded through the remaining peer
1949         #
1950         self.pg0.add_stream(pkts)
1951         self.pg_enable_capture(self.pg_interfaces)
1952         self.pg_start()
1953
1954         rx0 = self.pg3.get_capture(NUM_PKTS)
1955         self.assertEqual(len(pkts), len(rx0),
1956                          "Expected all (%s) packets across single path. "
1957                          "rx0: %s." % (len(pkts), len(rx0)))
1958
1959         #
1960         # put the connected routes back
1961         #
1962         self.pg2.config_ip4()
1963         self.pg2.resolve_arp()
1964
1965         self.pg0.add_stream(pkts)
1966         self.pg_enable_capture(self.pg_interfaces)
1967         self.pg_start()
1968
1969         rx0 = self.pg2._get_capture(NUM_PKTS)
1970         rx1 = self.pg3._get_capture(NUM_PKTS)
1971         self.assertNotEqual(0, len(rx0))
1972         self.assertNotEqual(0, len(rx1))
1973         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1974                          "Expected all (%s) packets across both ECMP paths. "
1975                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1976
1977     def test_mpls_v6_ebgp_pic(self):
1978         """ MPLSv6 eBGP Prefix-Independent Convergence (PIC) edge convergence
1979
1980         1) setup many eBGP VPNv6 routes via a pair of eBGP peers
1981         2) Check EMCP forwarding to these peers
1982         3) withdraw one eBGP path - expect LB across remaining eBGP
1983         """
1984
1985         #
1986         # Lot's of VPN routes. We need more the 64 so VPP will build
1987         # the fast convergence indirection
1988         #
1989         vpn_routes = []
1990         vpn_bindings = []
1991         pkts = []
1992         for ii in range(NUM_PKTS):
1993             dst = "3000::%d" % ii
1994             local_label = 1600 + ii
1995             vpn_routes.append(VppIpRoute(
1996                 self, dst, 128,
1997                 [VppRoutePath(
1998                     self.pg2.remote_ip6,
1999                     0xffffffff,
2000                     nh_table_id=1,
2001                     flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED),
2002                  VppRoutePath(
2003                      self.pg3.remote_ip6,
2004                      0xffffffff,
2005                      nh_table_id=1,
2006                      flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED)],
2007                 table_id=1))
2008             vpn_routes[ii].add_vpp_config()
2009
2010             vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 128,
2011                                               ip_table_id=1))
2012             vpn_bindings[ii].add_vpp_config()
2013
2014             pkts.append(Ether(dst=self.pg0.local_mac,
2015                               src=self.pg0.remote_mac) /
2016                         MPLS(label=local_label, ttl=64) /
2017                         IPv6(src=self.pg0.remote_ip6, dst=dst) /
2018                         UDP(sport=1234, dport=1234) /
2019                         Raw(b'\xa5' * 100))
2020             self.logger.info(self.vapi.cli("sh ip6 fib %s" % dst))
2021
2022         self.pg0.add_stream(pkts)
2023         self.pg_enable_capture(self.pg_interfaces)
2024         self.pg_start()
2025
2026         rx0 = self.pg2._get_capture(NUM_PKTS)
2027         rx1 = self.pg3._get_capture(NUM_PKTS)
2028         self.assertNotEqual(0, len(rx0))
2029         self.assertNotEqual(0, len(rx1))
2030         self.assertEqual(len(pkts), len(rx0) + len(rx1),
2031                          "Expected all (%s) packets across both ECMP paths. "
2032                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
2033
2034         #
2035         # use a test CLI command to stop the FIB walk process, this
2036         # will prevent the FIB converging the VPN routes and thus allow
2037         # us to probe the interim (post-fail, pre-converge) state
2038         #
2039         self.vapi.ppcli("test fib-walk-process disable")
2040
2041         #
2042         # withdraw the connected prefix on the interface.
2043         # and shutdown the interface so the ND cache is flushed.
2044         #
2045         self.pg2.unconfig_ip6()
2046         self.pg2.admin_down()
2047
2048         #
2049         # now all packets should be forwarded through the remaining peer
2050         #
2051         self.pg0.add_stream(pkts)
2052         self.pg_enable_capture(self.pg_interfaces)
2053         self.pg_start()
2054
2055         rx0 = self.pg3.get_capture(NUM_PKTS)
2056         self.assertEqual(len(pkts), len(rx0),
2057                          "Expected all (%s) packets across single path. "
2058                          "rx0: %s." % (len(pkts), len(rx0)))
2059
2060         #
2061         # enable the FIB walk process to converge the FIB
2062         #
2063         self.vapi.ppcli("test fib-walk-process enable")
2064         self.pg0.add_stream(pkts)
2065         self.pg_enable_capture(self.pg_interfaces)
2066         self.pg_start()
2067
2068         rx0 = self.pg3.get_capture(NUM_PKTS)
2069         self.assertEqual(len(pkts), len(rx0),
2070                          "Expected all (%s) packets across single path. "
2071                          "rx0: %s." % (len(pkts), len(rx0)))
2072
2073         #
2074         # put the connected routes back
2075         #
2076         self.logger.info(self.vapi.cli("sh log"))
2077         self.pg2.admin_up()
2078         self.pg2.config_ip6()
2079         self.pg2.resolve_ndp()
2080
2081         self.pg0.add_stream(pkts)
2082         self.pg_enable_capture(self.pg_interfaces)
2083         self.pg_start()
2084
2085         rx0 = self.pg2._get_capture(NUM_PKTS)
2086         rx1 = self.pg3._get_capture(NUM_PKTS)
2087         self.assertNotEqual(0, len(rx0))
2088         self.assertNotEqual(0, len(rx1))
2089         self.assertEqual(len(pkts), len(rx0) + len(rx1),
2090                          "Expected all (%s) packets across both ECMP paths. "
2091                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
2092
2093
2094 class TestMPLSL2(VppTestCase):
2095     """ MPLS-L2 """
2096
2097     @classmethod
2098     def setUpClass(cls):
2099         super(TestMPLSL2, cls).setUpClass()
2100
2101     @classmethod
2102     def tearDownClass(cls):
2103         super(TestMPLSL2, cls).tearDownClass()
2104
2105     def setUp(self):
2106         super(TestMPLSL2, self).setUp()
2107
2108         # create 2 pg interfaces
2109         self.create_pg_interfaces(range(2))
2110
2111         # create the default MPLS table
2112         self.tables = []
2113         tbl = VppMplsTable(self, 0)
2114         tbl.add_vpp_config()
2115         self.tables.append(tbl)
2116
2117         # use pg0 as the core facing interface, don't resolve ARP
2118         self.pg0.admin_up()
2119         self.pg0.config_ip4()
2120         self.pg0.enable_mpls()
2121
2122         # use the other 2 for customer facing L2 links
2123         for i in self.pg_interfaces[1:]:
2124             i.admin_up()
2125
2126     def tearDown(self):
2127         for i in self.pg_interfaces[1:]:
2128             i.admin_down()
2129
2130         self.pg0.disable_mpls()
2131         self.pg0.unconfig_ip4()
2132         self.pg0.admin_down()
2133         super(TestMPLSL2, self).tearDown()
2134
2135     def verify_capture_tunneled_ethernet(self, capture, sent, mpls_labels):
2136         capture = verify_filter(capture, sent)
2137
2138         self.assertEqual(len(capture), len(sent))
2139
2140         for i in range(len(capture)):
2141             tx = sent[i]
2142             rx = capture[i]
2143
2144             # the MPLS TTL is 255 since it enters a new tunnel
2145             verify_mpls_stack(self, rx, mpls_labels)
2146
2147             tx_eth = tx[Ether]
2148             rx_eth = Ether(scapy.compat.raw(rx[MPLS].payload))
2149
2150             self.assertEqual(rx_eth.src, tx_eth.src)
2151             self.assertEqual(rx_eth.dst, tx_eth.dst)
2152
2153     def verify_arp_req(self, rx, smac, sip, dip):
2154         ether = rx[Ether]
2155         self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
2156         self.assertEqual(ether.src, smac)
2157
2158         arp = rx[ARP]
2159         self.assertEqual(arp.hwtype, 1)
2160         self.assertEqual(arp.ptype, 0x800)
2161         self.assertEqual(arp.hwlen, 6)
2162         self.assertEqual(arp.plen, 4)
2163         self.assertEqual(arp.op, ARP.who_has)
2164         self.assertEqual(arp.hwsrc, smac)
2165         self.assertEqual(arp.hwdst, "00:00:00:00:00:00")
2166         self.assertEqual(arp.psrc, sip)
2167         self.assertEqual(arp.pdst, dip)
2168
2169     def test_vpws(self):
2170         """ Virtual Private Wire Service """
2171
2172         #
2173         # Create an MPLS tunnel that pushes 1 label
2174         # For Ethernet over MPLS the uniform mode is irrelevant since ttl/cos
2175         # information is not in the packet, but we test it works anyway
2176         #
2177         mpls_tun_1 = VppMPLSTunnelInterface(
2178             self,
2179             [VppRoutePath(self.pg0.remote_ip4,
2180                           self.pg0.sw_if_index,
2181                           labels=[VppMplsLabel(42, MplsLspMode.UNIFORM)])],
2182             is_l2=1)
2183         mpls_tun_1.add_vpp_config()
2184         mpls_tun_1.admin_up()
2185
2186         #
2187         # Create a label entry to for 55 that does L2 input to the tunnel
2188         #
2189         route_55_eos = VppMplsRoute(
2190             self, 55, 1,
2191             [VppRoutePath("0.0.0.0",
2192                           mpls_tun_1.sw_if_index,
2193                           type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2194                           proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)],
2195             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)
2196         route_55_eos.add_vpp_config()
2197
2198         #
2199         # Cross-connect the tunnel with one of the customers L2 interfaces
2200         #
2201         self.vapi.sw_interface_set_l2_xconnect(self.pg1.sw_if_index,
2202                                                mpls_tun_1.sw_if_index,
2203                                                enable=1)
2204         self.vapi.sw_interface_set_l2_xconnect(mpls_tun_1.sw_if_index,
2205                                                self.pg1.sw_if_index,
2206                                                enable=1)
2207
2208         #
2209         # inject a packet from the core
2210         #
2211         pcore = (Ether(dst=self.pg0.local_mac,
2212                        src=self.pg0.remote_mac) /
2213                  MPLS(label=55, ttl=64) /
2214                  Ether(dst="00:00:de:ad:ba:be",
2215                        src="00:00:de:ad:be:ef") /
2216                  IP(src="10.10.10.10", dst="11.11.11.11") /
2217                  UDP(sport=1234, dport=1234) /
2218                  Raw(b'\xa5' * 100))
2219
2220         tx0 = pcore * NUM_PKTS
2221         rx0 = self.send_and_expect(self.pg0, tx0, self.pg1)
2222         payload = pcore[MPLS].payload
2223
2224         self.assertEqual(rx0[0][Ether].dst, payload[Ether].dst)
2225         self.assertEqual(rx0[0][Ether].src, payload[Ether].src)
2226
2227         #
2228         # Inject a packet from the customer/L2 side
2229         # there's no resolved ARP entry so the first packet we see should be
2230         # an ARP request
2231         #
2232         tx1 = pcore[MPLS].payload
2233         rx1 = self.send_and_expect(self.pg1, [tx1], self.pg0)
2234
2235         self.verify_arp_req(rx1[0],
2236                             self.pg0.local_mac,
2237                             self.pg0.local_ip4,
2238                             self.pg0.remote_ip4)
2239
2240         #
2241         # resolve the ARP entries and send again
2242         #
2243         self.pg0.resolve_arp()
2244         tx1 = pcore[MPLS].payload * NUM_PKTS
2245         rx1 = self.send_and_expect(self.pg1, tx1, self.pg0)
2246
2247         self.verify_capture_tunneled_ethernet(rx1, tx1, [VppMplsLabel(42)])
2248
2249     def test_vpls(self):
2250         """ Virtual Private LAN Service """
2251
2252         # we skipped this in the setup
2253         self.pg0.resolve_arp()
2254
2255         #
2256         # Create a L2 MPLS tunnels
2257         #
2258         mpls_tun1 = VppMPLSTunnelInterface(
2259             self,
2260             [VppRoutePath(self.pg0.remote_ip4,
2261                           self.pg0.sw_if_index,
2262                           labels=[VppMplsLabel(42)])],
2263             is_l2=1)
2264         mpls_tun1.add_vpp_config()
2265         mpls_tun1.admin_up()
2266
2267         mpls_tun2 = VppMPLSTunnelInterface(
2268             self,
2269             [VppRoutePath(self.pg0.remote_ip4,
2270                           self.pg0.sw_if_index,
2271                           labels=[VppMplsLabel(43)])],
2272             is_l2=1)
2273         mpls_tun2.add_vpp_config()
2274         mpls_tun2.admin_up()
2275
2276         #
2277         # Create a label entries, 55 and 56, that do L2 input to the tunnel
2278         # the latter includes a Psuedo Wire Control Word
2279         #
2280         route_55_eos = VppMplsRoute(
2281             self, 55, 1,
2282             [VppRoutePath("0.0.0.0",
2283                           mpls_tun1.sw_if_index,
2284                           type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2285                           proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)],
2286             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)
2287
2288         route_56_eos = VppMplsRoute(
2289             self, 56, 1,
2290             [VppRoutePath("0.0.0.0",
2291                           mpls_tun2.sw_if_index,
2292                           type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2293                           flags=FibPathFlags.FIB_PATH_FLAG_POP_PW_CW,
2294                           proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)],
2295             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)
2296
2297         # move me
2298         route_56_eos.add_vpp_config()
2299         route_55_eos.add_vpp_config()
2300
2301         self.logger.info(self.vapi.cli("sh mpls fib 56"))
2302
2303         #
2304         # add to tunnel to the customers bridge-domain
2305         #
2306         self.vapi.sw_interface_set_l2_bridge(
2307             rx_sw_if_index=mpls_tun1.sw_if_index, bd_id=1)
2308         self.vapi.sw_interface_set_l2_bridge(
2309             rx_sw_if_index=mpls_tun2.sw_if_index, bd_id=1)
2310         self.vapi.sw_interface_set_l2_bridge(
2311             rx_sw_if_index=self.pg1.sw_if_index, bd_id=1)
2312
2313         #
2314         # Packet from host on the customer interface to each host
2315         # reachable over the core, and vice-versa
2316         #
2317         p_cust1 = (Ether(dst="00:00:de:ad:ba:b1",
2318                          src="00:00:de:ad:be:ef") /
2319                    IP(src="10.10.10.10", dst="11.11.11.11") /
2320                    UDP(sport=1234, dport=1234) /
2321                    Raw(b'\xa5' * 100))
2322         p_cust2 = (Ether(dst="00:00:de:ad:ba:b2",
2323                          src="00:00:de:ad:be:ef") /
2324                    IP(src="10.10.10.10", dst="11.11.11.12") /
2325                    UDP(sport=1234, dport=1234) /
2326                    Raw(b'\xa5' * 100))
2327         p_core1 = (Ether(dst=self.pg0.local_mac,
2328                          src=self.pg0.remote_mac) /
2329                    MPLS(label=55, ttl=64) /
2330                    Ether(src="00:00:de:ad:ba:b1",
2331                          dst="00:00:de:ad:be:ef") /
2332                    IP(dst="10.10.10.10", src="11.11.11.11") /
2333                    UDP(sport=1234, dport=1234) /
2334                    Raw(b'\xa5' * 100))
2335         p_core2 = (Ether(dst=self.pg0.local_mac,
2336                          src=self.pg0.remote_mac) /
2337                    MPLS(label=56, ttl=64) /
2338                    Raw(b'\x01' * 4) /  # PW CW
2339                    Ether(src="00:00:de:ad:ba:b2",
2340                          dst="00:00:de:ad:be:ef") /
2341                    IP(dst="10.10.10.10", src="11.11.11.12") /
2342                    UDP(sport=1234, dport=1234) /
2343                    Raw(b'\xa5' * 100))
2344
2345         #
2346         # The BD is learning, so send in one of each packet to learn
2347         #
2348
2349         # 2 packets due to BD flooding
2350         rx = self.send_and_expect(self.pg1, p_cust1, self.pg0, n_rx=2)
2351         rx = self.send_and_expect(self.pg1, p_cust2, self.pg0, n_rx=2)
2352
2353         # we've learnt this so expect it be be forwarded not flooded
2354         rx = self.send_and_expect(self.pg0, [p_core1], self.pg1)
2355         self.assertEqual(rx[0][Ether].dst, p_cust1[Ether].src)
2356         self.assertEqual(rx[0][Ether].src, p_cust1[Ether].dst)
2357
2358         rx = self.send_and_expect(self.pg0, [p_core2], self.pg1)
2359         self.assertEqual(rx[0][Ether].dst, p_cust2[Ether].src)
2360         self.assertEqual(rx[0][Ether].src, p_cust2[Ether].dst)
2361
2362         #
2363         # now a stream in each direction from each host
2364         #
2365         rx = self.send_and_expect(self.pg1, p_cust1 * NUM_PKTS, self.pg0)
2366         self.verify_capture_tunneled_ethernet(rx, p_cust1 * NUM_PKTS,
2367                                               [VppMplsLabel(42)])
2368
2369         rx = self.send_and_expect(self.pg1, p_cust2 * NUM_PKTS, self.pg0)
2370         self.verify_capture_tunneled_ethernet(rx, p_cust2 * NUM_PKTS,
2371                                               [VppMplsLabel(43)])
2372
2373         rx = self.send_and_expect(self.pg0, p_core1 * NUM_PKTS, self.pg1)
2374         rx = self.send_and_expect(self.pg0, p_core2 * NUM_PKTS, self.pg1)
2375
2376         #
2377         # remove interfaces from customers bridge-domain
2378         #
2379         self.vapi.sw_interface_set_l2_bridge(
2380             rx_sw_if_index=mpls_tun1.sw_if_index, bd_id=1, enable=0)
2381         self.vapi.sw_interface_set_l2_bridge(
2382             rx_sw_if_index=mpls_tun2.sw_if_index, bd_id=1, enable=0)
2383         self.vapi.sw_interface_set_l2_bridge(
2384             rx_sw_if_index=self.pg1.sw_if_index, bd_id=1, enable=0)
2385
2386
2387 if __name__ == '__main__':
2388     unittest.main(testRunner=VppTestRunner)