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