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