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