32868c691577af88a6bbe9dbbac72a345e1d08fe
[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     MRouteItfFlags, MRouteEntryFlags, VppIpTable, VppMplsTable, \
11     VppMplsLabel, MplsLspMode, find_mpls_route, \
12     FibPathProto, FibPathType, FibPathFlags, VppMplsLabel, MplsLspMode
13 from vpp_mpls_tunnel_interface import VppMPLSTunnelInterface
14
15 import scapy.compat
16 from scapy.packet import Raw
17 from scapy.layers.l2 import Ether, ARP
18 from scapy.layers.inet import IP, UDP, ICMP
19 from scapy.layers.inet6 import IPv6, ICMPv6TimeExceeded
20 from scapy.contrib.mpls import MPLS
21
22 NUM_PKTS = 67
23
24 # scapy removed these attributes.
25 # we asked that they be restored: https://github.com/secdev/scapy/pull/1878
26 # semantic names have more meaning than numbers. so here they are.
27 ARP.who_has = 1
28 ARP.is_at = 2
29
30
31 def verify_filter(capture, sent):
32     if not len(capture) == len(sent):
33         # filter out any IPv6 RAs from the capture
34         for p in capture:
35             if p.haslayer(IPv6):
36                 capture.remove(p)
37     return capture
38
39
40 def verify_mpls_stack(tst, rx, mpls_labels):
41     # the rx'd packet has the MPLS label popped
42     eth = rx[Ether]
43     tst.assertEqual(eth.type, 0x8847)
44
45     rx_mpls = rx[MPLS]
46
47     for ii in range(len(mpls_labels)):
48         tst.assertEqual(rx_mpls.label, mpls_labels[ii].value)
49         tst.assertEqual(rx_mpls.cos, mpls_labels[ii].exp)
50         tst.assertEqual(rx_mpls.ttl, mpls_labels[ii].ttl)
51
52         if ii == len(mpls_labels) - 1:
53             tst.assertEqual(rx_mpls.s, 1)
54         else:
55             # not end of stack
56             tst.assertEqual(rx_mpls.s, 0)
57             # pop the label to expose the next
58             rx_mpls = rx_mpls[MPLS].payload
59
60
61 class TestMPLS(VppTestCase):
62     """ MPLS Test Case """
63
64     @classmethod
65     def setUpClass(cls):
66         super(TestMPLS, cls).setUpClass()
67
68     @classmethod
69     def tearDownClass(cls):
70         super(TestMPLS, cls).tearDownClass()
71
72     def setUp(self):
73         super(TestMPLS, self).setUp()
74
75         # create 2 pg interfaces
76         self.create_pg_interfaces(range(4))
77
78         # setup both interfaces
79         # assign them different tables.
80         table_id = 0
81         self.tables = []
82
83         tbl = VppMplsTable(self, 0)
84         tbl.add_vpp_config()
85         self.tables.append(tbl)
86
87         for i in self.pg_interfaces:
88             i.admin_up()
89
90             if table_id != 0:
91                 tbl = VppIpTable(self, table_id)
92                 tbl.add_vpp_config()
93                 self.tables.append(tbl)
94                 tbl = VppIpTable(self, table_id, is_ip6=1)
95                 tbl.add_vpp_config()
96                 self.tables.append(tbl)
97
98             i.set_table_ip4(table_id)
99             i.set_table_ip6(table_id)
100             i.config_ip4()
101             i.resolve_arp()
102             i.config_ip6()
103             i.resolve_ndp()
104             i.enable_mpls()
105             table_id += 1
106
107     def tearDown(self):
108         for i in self.pg_interfaces:
109             i.unconfig_ip4()
110             i.unconfig_ip6()
111             i.ip6_disable()
112             i.set_table_ip4(0)
113             i.set_table_ip6(0)
114             i.disable_mpls()
115             i.admin_down()
116         super(TestMPLS, self).tearDown()
117
118     # the default of 64 matches the IP packet TTL default
119     def create_stream_labelled_ip4(
120             self,
121             src_if,
122             mpls_labels,
123             ping=0,
124             ip_itf=None,
125             dst_ip=None,
126             chksum=None,
127             ip_ttl=64,
128             n=257):
129         self.reset_packet_infos()
130         pkts = []
131         for i in range(0, n):
132             info = self.create_packet_info(src_if, src_if)
133             payload = self.info_to_payload(info)
134             p = Ether(dst=src_if.local_mac, src=src_if.remote_mac)
135
136             for ii in range(len(mpls_labels)):
137                 p = p / MPLS(label=mpls_labels[ii].value,
138                              ttl=mpls_labels[ii].ttl,
139                              cos=mpls_labels[ii].exp)
140             if not ping:
141                 if not dst_ip:
142                     p = (p / IP(src=src_if.local_ip4,
143                                 dst=src_if.remote_ip4,
144                                 ttl=ip_ttl) /
145                          UDP(sport=1234, dport=1234) /
146                          Raw(payload))
147                 else:
148                     p = (p / IP(src=src_if.local_ip4, dst=dst_ip, ttl=ip_ttl) /
149                          UDP(sport=1234, dport=1234) /
150                          Raw(payload))
151             else:
152                 p = (p / IP(src=ip_itf.remote_ip4,
153                             dst=ip_itf.local_ip4,
154                             ttl=ip_ttl) /
155                      ICMP())
156
157             if chksum:
158                 p[IP].chksum = chksum
159             info.data = p.copy()
160             pkts.append(p)
161         return pkts
162
163     def create_stream_ip4(self, src_if, dst_ip, ip_ttl=64,
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         #
1287         # Create a multicast tunnel with two replications
1288         #
1289         mpls_tun = VppMPLSTunnelInterface(
1290             self,
1291             [VppRoutePath(self.pg2.remote_ip4,
1292                           self.pg2.sw_if_index,
1293                           labels=[VppMplsLabel(42)]),
1294              VppRoutePath(self.pg3.remote_ip4,
1295                           self.pg3.sw_if_index,
1296                           labels=[VppMplsLabel(43)])],
1297             is_multicast=1)
1298         mpls_tun.add_vpp_config()
1299         mpls_tun.admin_up()
1300
1301         #
1302         # add an unlabelled route through the new tunnel
1303         #
1304         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
1305                                     [VppRoutePath("0.0.0.0",
1306                                                   mpls_tun._sw_if_index)])
1307         route_10_0_0_3.add_vpp_config()
1308
1309         self.vapi.cli("clear trace")
1310         tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
1311         self.pg0.add_stream(tx)
1312
1313         self.pg_enable_capture(self.pg_interfaces)
1314         self.pg_start()
1315
1316         rx = self.pg2.get_capture(257)
1317         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(42)])
1318         rx = self.pg3.get_capture(257)
1319         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(43)])
1320
1321         #
1322         # An an IP multicast route via the tunnel
1323         # A (*,G).
1324         # one accepting interface, pg0, 1 forwarding interface via the tunnel
1325         #
1326         route_232_1_1_1 = VppIpMRoute(
1327             self,
1328             "0.0.0.0",
1329             "232.1.1.1", 32,
1330             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
1331             [VppMRoutePath(self.pg0.sw_if_index,
1332                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
1333              VppMRoutePath(mpls_tun._sw_if_index,
1334                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
1335         route_232_1_1_1.add_vpp_config()
1336         self.logger.info(self.vapi.cli("sh ip mfib index 0"))
1337
1338         self.vapi.cli("clear trace")
1339         tx = self.create_stream_ip4(self.pg0, "232.1.1.1")
1340         self.pg0.add_stream(tx)
1341
1342         self.pg_enable_capture(self.pg_interfaces)
1343         self.pg_start()
1344
1345         rx = self.pg2.get_capture(257)
1346         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(42)])
1347         rx = self.pg3.get_capture(257)
1348         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [VppMplsLabel(43)])
1349
1350     def test_mcast_ip4_tail(self):
1351         """ MPLS IPv4 Multicast Tail """
1352
1353         #
1354         # Add a multicast route that will forward the traffic
1355         # post-disposition
1356         #
1357         route_232_1_1_1 = VppIpMRoute(
1358             self,
1359             "0.0.0.0",
1360             "232.1.1.1", 32,
1361             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
1362             table_id=1,
1363             paths=[VppMRoutePath(self.pg1.sw_if_index,
1364                                  MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
1365         route_232_1_1_1.add_vpp_config()
1366
1367         #
1368         # An interface receive label that maps traffic to RX on interface
1369         # pg1
1370         # by injecting the packet in on pg0, which is in table 0
1371         # doing an rpf-id  and matching a route in table 1
1372         # if the packet egresses, then we must have matched the route in
1373         # table 1
1374         #
1375         route_34_eos = VppMplsRoute(
1376             self, 34, 1,
1377             [VppRoutePath("0.0.0.0",
1378                           0xffffffff,
1379                           nh_table_id=1,
1380                           rpf_id=55)],
1381             is_multicast=1,
1382             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP4)
1383
1384         route_34_eos.add_vpp_config()
1385
1386         #
1387         # Drop due to interface lookup miss
1388         #
1389         self.vapi.cli("clear trace")
1390         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(34)],
1391                                              dst_ip="232.1.1.1", n=1)
1392         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop none")
1393
1394         #
1395         # set the RPF-ID of the entry to match the input packet's
1396         #
1397         route_232_1_1_1.update_rpf_id(55)
1398         self.logger.info(self.vapi.cli("sh ip mfib index 1 232.1.1.1"))
1399
1400         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(34)],
1401                                              dst_ip="232.1.1.1")
1402         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1403         self.verify_capture_ip4(self.pg1, rx, tx)
1404
1405         #
1406         # disposed packets have an invalid IPv4 checksum
1407         #
1408         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(34)],
1409                                              dst_ip="232.1.1.1", n=65,
1410                                              chksum=1)
1411         self.send_and_assert_no_replies(self.pg0, tx, "Invalid Checksum")
1412
1413         #
1414         # set the RPF-ID of the entry to not match the input packet's
1415         #
1416         route_232_1_1_1.update_rpf_id(56)
1417         tx = self.create_stream_labelled_ip4(self.pg0, [VppMplsLabel(34)],
1418                                              dst_ip="232.1.1.1")
1419         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
1420
1421     def test_mcast_ip6_tail(self):
1422         """ MPLS IPv6 Multicast Tail """
1423
1424         #
1425         # Add a multicast route that will forward the traffic
1426         # post-disposition
1427         #
1428         route_ff = VppIpMRoute(
1429             self,
1430             "::",
1431             "ff01::1", 32,
1432             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
1433             table_id=1,
1434             paths=[VppMRoutePath(self.pg1.sw_if_index,
1435                                  MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
1436                                  proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)])
1437         route_ff.add_vpp_config()
1438
1439         #
1440         # An interface receive label that maps traffic to RX on interface
1441         # pg1
1442         # by injecting the packet in on pg0, which is in table 0
1443         # doing an rpf-id  and matching a route in table 1
1444         # if the packet egresses, then we must have matched the route in
1445         # table 1
1446         #
1447         route_34_eos = VppMplsRoute(
1448             self, 34, 1,
1449             [VppRoutePath("::",
1450                           0xffffffff,
1451                           nh_table_id=1,
1452                           rpf_id=55)],
1453             is_multicast=1,
1454             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)
1455
1456         route_34_eos.add_vpp_config()
1457
1458         #
1459         # Drop due to interface lookup miss
1460         #
1461         tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(34)],
1462                                              dst_ip="ff01::1")
1463         self.send_and_assert_no_replies(self.pg0, tx, "RPF Miss")
1464
1465         #
1466         # set the RPF-ID of the entry to match the input packet's
1467         #
1468         route_ff.update_rpf_id(55)
1469
1470         tx = self.create_stream_labelled_ip6(self.pg0, [VppMplsLabel(34)],
1471                                              dst_ip="ff01::1")
1472         rx = self.send_and_expect(self.pg0, tx, self.pg1)
1473         self.verify_capture_ip6(self.pg1, rx, tx)
1474
1475         #
1476         # disposed packets have hop-limit = 1
1477         #
1478         tx = self.create_stream_labelled_ip6(self.pg0,
1479                                              [VppMplsLabel(34)],
1480                                              dst_ip="ff01::1",
1481                                              hlim=1)
1482         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1483         self.verify_capture_ip6_icmp(self.pg0, rx, tx)
1484
1485         #
1486         # set the RPF-ID of the entry to not match the input packet's
1487         #
1488         route_ff.update_rpf_id(56)
1489         tx = self.create_stream_labelled_ip6(self.pg0,
1490                                              [VppMplsLabel(34)],
1491                                              dst_ip="ff01::1")
1492         self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
1493
1494     def test_6pe(self):
1495         """ MPLS 6PE """
1496
1497         #
1498         # Add a non-recursive route with a single out label
1499         #
1500         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
1501                                     [VppRoutePath(self.pg0.remote_ip4,
1502                                                   self.pg0.sw_if_index,
1503                                                   labels=[VppMplsLabel(45)])])
1504         route_10_0_0_1.add_vpp_config()
1505
1506         # bind a local label to the route
1507         binding = VppMplsIpBind(self, 44, "10.0.0.1", 32)
1508         binding.add_vpp_config()
1509
1510         #
1511         # a labelled v6 route that resolves through the v4
1512         #
1513         route_2001_3 = VppIpRoute(
1514             self, "2001::3", 128,
1515             [VppRoutePath("10.0.0.1",
1516                           INVALID_INDEX,
1517                           labels=[VppMplsLabel(32)])])
1518         route_2001_3.add_vpp_config()
1519
1520         tx = self.create_stream_ip6(self.pg0, "2001::3")
1521         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1522
1523         self.verify_capture_labelled_ip6(self.pg0, rx, tx,
1524                                          [VppMplsLabel(45),
1525                                           VppMplsLabel(32)])
1526
1527         #
1528         # and a v4 recursive via the v6
1529         #
1530         route_20_3 = VppIpRoute(
1531             self, "20.0.0.3", 32,
1532             [VppRoutePath("2001::3",
1533                           INVALID_INDEX,
1534                           labels=[VppMplsLabel(99)])])
1535         route_20_3.add_vpp_config()
1536
1537         tx = self.create_stream_ip4(self.pg0, "20.0.0.3")
1538         rx = self.send_and_expect(self.pg0, tx, self.pg0)
1539
1540         self.verify_capture_labelled_ip4(self.pg0, rx, tx,
1541                                          [VppMplsLabel(45),
1542                                           VppMplsLabel(32),
1543                                           VppMplsLabel(99)])
1544
1545
1546 class TestMPLSDisabled(VppTestCase):
1547     """ MPLS disabled """
1548
1549     @classmethod
1550     def setUpClass(cls):
1551         super(TestMPLSDisabled, cls).setUpClass()
1552
1553     @classmethod
1554     def tearDownClass(cls):
1555         super(TestMPLSDisabled, cls).tearDownClass()
1556
1557     def setUp(self):
1558         super(TestMPLSDisabled, self).setUp()
1559
1560         # create 2 pg interfaces
1561         self.create_pg_interfaces(range(2))
1562
1563         self.tbl = VppMplsTable(self, 0)
1564         self.tbl.add_vpp_config()
1565
1566         # PG0 is MPLS enabled
1567         self.pg0.admin_up()
1568         self.pg0.config_ip4()
1569         self.pg0.resolve_arp()
1570         self.pg0.enable_mpls()
1571
1572         # PG 1 is not MPLS enabled
1573         self.pg1.admin_up()
1574
1575     def tearDown(self):
1576         for i in self.pg_interfaces:
1577             i.unconfig_ip4()
1578             i.admin_down()
1579
1580         self.pg0.disable_mpls()
1581         super(TestMPLSDisabled, self).tearDown()
1582
1583     def test_mpls_disabled(self):
1584         """ MPLS Disabled """
1585
1586         tx = (Ether(src=self.pg1.remote_mac,
1587                     dst=self.pg1.local_mac) /
1588               MPLS(label=32, ttl=64) /
1589               IPv6(src="2001::1", dst=self.pg0.remote_ip6) /
1590               UDP(sport=1234, dport=1234) /
1591               Raw(b'\xa5' * 100))
1592
1593         #
1594         # A simple MPLS xconnect - eos label in label out
1595         #
1596         route_32_eos = VppMplsRoute(self, 32, 1,
1597                                     [VppRoutePath(self.pg0.remote_ip4,
1598                                                   self.pg0.sw_if_index,
1599                                                   labels=[33])])
1600         route_32_eos.add_vpp_config()
1601
1602         #
1603         # PG1 does not forward IP traffic
1604         #
1605         self.send_and_assert_no_replies(self.pg1, tx, "MPLS disabled")
1606
1607         #
1608         # MPLS enable PG1
1609         #
1610         self.pg1.enable_mpls()
1611
1612         #
1613         # Now we get packets through
1614         #
1615         self.pg1.add_stream(tx)
1616         self.pg_enable_capture(self.pg_interfaces)
1617         self.pg_start()
1618
1619         rx = self.pg0.get_capture(1)
1620
1621         #
1622         # Disable PG1
1623         #
1624         self.pg1.disable_mpls()
1625
1626         #
1627         # PG1 does not forward IP traffic
1628         #
1629         self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1630         self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1631
1632
1633 class TestMPLSPIC(VppTestCase):
1634     """ MPLS Prefix-Independent Convergence (PIC) edge convergence """
1635
1636     @classmethod
1637     def setUpClass(cls):
1638         super(TestMPLSPIC, cls).setUpClass()
1639
1640     @classmethod
1641     def tearDownClass(cls):
1642         super(TestMPLSPIC, cls).tearDownClass()
1643
1644     def setUp(self):
1645         super(TestMPLSPIC, self).setUp()
1646
1647         # create 2 pg interfaces
1648         self.create_pg_interfaces(range(4))
1649
1650         mpls_tbl = VppMplsTable(self, 0)
1651         mpls_tbl.add_vpp_config()
1652         tbl4 = VppIpTable(self, 1)
1653         tbl4.add_vpp_config()
1654         tbl6 = VppIpTable(self, 1, is_ip6=1)
1655         tbl6.add_vpp_config()
1656
1657         # core links
1658         self.pg0.admin_up()
1659         self.pg0.config_ip4()
1660         self.pg0.resolve_arp()
1661         self.pg0.enable_mpls()
1662
1663         self.pg1.admin_up()
1664         self.pg1.config_ip4()
1665         self.pg1.resolve_arp()
1666         self.pg1.enable_mpls()
1667
1668         # VRF (customer facing) link
1669         self.pg2.admin_up()
1670         self.pg2.set_table_ip4(1)
1671         self.pg2.config_ip4()
1672         self.pg2.resolve_arp()
1673         self.pg2.set_table_ip6(1)
1674         self.pg2.config_ip6()
1675         self.pg2.resolve_ndp()
1676
1677         self.pg3.admin_up()
1678         self.pg3.set_table_ip4(1)
1679         self.pg3.config_ip4()
1680         self.pg3.resolve_arp()
1681         self.pg3.set_table_ip6(1)
1682         self.pg3.config_ip6()
1683         self.pg3.resolve_ndp()
1684
1685     def tearDown(self):
1686         self.pg0.disable_mpls()
1687         self.pg1.disable_mpls()
1688         for i in self.pg_interfaces:
1689             i.unconfig_ip4()
1690             i.unconfig_ip6()
1691             i.set_table_ip4(0)
1692             i.set_table_ip6(0)
1693             i.admin_down()
1694         super(TestMPLSPIC, self).tearDown()
1695
1696     def test_mpls_ibgp_pic(self):
1697         """ MPLS iBGP Prefix-Independent Convergence (PIC) edge convergence
1698
1699         1) setup many iBGP VPN routes via a pair of iBGP peers.
1700         2) Check EMCP forwarding to these peers
1701         3) withdraw the IGP route to one of these peers.
1702         4) check forwarding continues to the remaining peer
1703         """
1704
1705         #
1706         # IGP+LDP core routes
1707         #
1708         core_10_0_0_45 = VppIpRoute(self, "10.0.0.45", 32,
1709                                     [VppRoutePath(self.pg0.remote_ip4,
1710                                                   self.pg0.sw_if_index,
1711                                                   labels=[45])])
1712         core_10_0_0_45.add_vpp_config()
1713
1714         core_10_0_0_46 = VppIpRoute(self, "10.0.0.46", 32,
1715                                     [VppRoutePath(self.pg1.remote_ip4,
1716                                                   self.pg1.sw_if_index,
1717                                                   labels=[46])])
1718         core_10_0_0_46.add_vpp_config()
1719
1720         #
1721         # Lot's of VPN routes. We need more the 64 so VPP will build
1722         # the fast convergence indirection
1723         #
1724         vpn_routes = []
1725         pkts = []
1726         for ii in range(NUM_PKTS):
1727             dst = "192.168.1.%d" % ii
1728             vpn_routes.append(VppIpRoute(
1729                 self, dst, 32,
1730                 [VppRoutePath(
1731                     "10.0.0.45",
1732                     0xffffffff,
1733                     labels=[145],
1734                     flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_HOST),
1735                  VppRoutePath(
1736                      "10.0.0.46",
1737                      0xffffffff,
1738                      labels=[146],
1739                      flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_HOST)],
1740                 table_id=1))
1741             vpn_routes[ii].add_vpp_config()
1742
1743             pkts.append(Ether(dst=self.pg2.local_mac,
1744                               src=self.pg2.remote_mac) /
1745                         IP(src=self.pg2.remote_ip4, dst=dst) /
1746                         UDP(sport=1234, dport=1234) /
1747                         Raw(b'\xa5' * 100))
1748
1749         #
1750         # Send the packet stream (one pkt to each VPN route)
1751         #  - expect a 50-50 split of the traffic
1752         #
1753         self.pg2.add_stream(pkts)
1754         self.pg_enable_capture(self.pg_interfaces)
1755         self.pg_start()
1756
1757         rx0 = self.pg0._get_capture(NUM_PKTS)
1758         rx1 = self.pg1._get_capture(NUM_PKTS)
1759
1760         # not testing the LB hashing algorithm so we're not concerned
1761         # with the split ratio, just as long as neither is 0
1762         self.assertNotEqual(0, len(rx0))
1763         self.assertNotEqual(0, len(rx1))
1764         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1765                          "Expected all (%s) packets across both ECMP paths. "
1766                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1767
1768         #
1769         # use a test CLI command to stop the FIB walk process, this
1770         # will prevent the FIB converging the VPN routes and thus allow
1771         # us to probe the interim (post-fail, pre-converge) state
1772         #
1773         self.vapi.ppcli("test fib-walk-process disable")
1774
1775         #
1776         # Withdraw one of the IGP routes
1777         #
1778         core_10_0_0_46.remove_vpp_config()
1779
1780         #
1781         # now all packets should be forwarded through the remaining peer
1782         #
1783         self.vapi.ppcli("clear trace")
1784         self.pg2.add_stream(pkts)
1785         self.pg_enable_capture(self.pg_interfaces)
1786         self.pg_start()
1787
1788         rx0 = self.pg0.get_capture(NUM_PKTS)
1789         self.assertEqual(len(pkts), len(rx0),
1790                          "Expected all (%s) packets across single path. "
1791                          "rx0: %s." % (len(pkts), len(rx0)))
1792
1793         #
1794         # enable the FIB walk process to converge the FIB
1795         #
1796         self.vapi.ppcli("test fib-walk-process enable")
1797
1798         #
1799         # packets should still be forwarded through the remaining peer
1800         #
1801         self.pg2.add_stream(pkts)
1802         self.pg_enable_capture(self.pg_interfaces)
1803         self.pg_start()
1804
1805         rx0 = self.pg0.get_capture(NUM_PKTS)
1806         self.assertEqual(len(pkts), len(rx0),
1807                          "Expected all (%s) packets across single path. "
1808                          "rx0: %s." % (len(pkts), len(rx0)))
1809
1810         #
1811         # Add the IGP route back and we return to load-balancing
1812         #
1813         core_10_0_0_46.add_vpp_config()
1814
1815         self.pg2.add_stream(pkts)
1816         self.pg_enable_capture(self.pg_interfaces)
1817         self.pg_start()
1818
1819         rx0 = self.pg0._get_capture(NUM_PKTS)
1820         rx1 = self.pg1._get_capture(NUM_PKTS)
1821         self.assertNotEqual(0, len(rx0))
1822         self.assertNotEqual(0, len(rx1))
1823         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1824                          "Expected all (%s) packets across both ECMP paths. "
1825                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1826
1827     def test_mpls_ebgp_pic(self):
1828         """ MPLS eBGP Prefix-Independent Convergence (PIC) edge convergence
1829
1830         1) setup many eBGP VPN routes via a pair of eBGP peers.
1831         2) Check EMCP forwarding to these peers
1832         3) withdraw one eBGP path - expect LB across remaining eBGP
1833         """
1834
1835         #
1836         # Lot's of VPN routes. We need more the 64 so VPP will build
1837         # the fast convergence indirection
1838         #
1839         vpn_routes = []
1840         vpn_bindings = []
1841         pkts = []
1842         for ii in range(NUM_PKTS):
1843             dst = "192.168.1.%d" % ii
1844             local_label = 1600 + ii
1845             vpn_routes.append(VppIpRoute(
1846                 self, dst, 32,
1847                 [VppRoutePath(
1848                     self.pg2.remote_ip4,
1849                     0xffffffff,
1850                     nh_table_id=1,
1851                     flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED),
1852                  VppRoutePath(
1853                      self.pg3.remote_ip4,
1854                      0xffffffff,
1855                      nh_table_id=1,
1856                      flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED)],
1857                 table_id=1))
1858             vpn_routes[ii].add_vpp_config()
1859
1860             vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 32,
1861                                               ip_table_id=1))
1862             vpn_bindings[ii].add_vpp_config()
1863
1864             pkts.append(Ether(dst=self.pg0.local_mac,
1865                               src=self.pg0.remote_mac) /
1866                         MPLS(label=local_label, ttl=64) /
1867                         IP(src=self.pg0.remote_ip4, dst=dst) /
1868                         UDP(sport=1234, dport=1234) /
1869                         Raw(b'\xa5' * 100))
1870
1871         #
1872         # Send the packet stream (one pkt to each VPN route)
1873         #  - expect a 50-50 split of the traffic
1874         #
1875         self.pg0.add_stream(pkts)
1876         self.pg_enable_capture(self.pg_interfaces)
1877         self.pg_start()
1878
1879         rx0 = self.pg2._get_capture(NUM_PKTS)
1880         rx1 = self.pg3._get_capture(NUM_PKTS)
1881
1882         # not testing the LB hashing algorithm so we're not concerned
1883         # with the split ratio, just as long as neither is 0
1884         self.assertNotEqual(0, len(rx0))
1885         self.assertNotEqual(0, len(rx1))
1886         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1887                          "Expected all (%s) packets across both ECMP paths. "
1888                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1889
1890         #
1891         # use a test CLI command to stop the FIB walk process, this
1892         # will prevent the FIB converging the VPN routes and thus allow
1893         # us to probe the interim (post-fail, pre-converge) state
1894         #
1895         self.vapi.ppcli("test fib-walk-process disable")
1896
1897         #
1898         # withdraw the connected prefix on the interface.
1899         #
1900         self.pg2.unconfig_ip4()
1901
1902         #
1903         # now all packets should be forwarded through the remaining peer
1904         #
1905         self.pg0.add_stream(pkts)
1906         self.pg_enable_capture(self.pg_interfaces)
1907         self.pg_start()
1908
1909         rx0 = self.pg3.get_capture(NUM_PKTS)
1910         self.assertEqual(len(pkts), len(rx0),
1911                          "Expected all (%s) packets across single path. "
1912                          "rx0: %s." % (len(pkts), len(rx0)))
1913
1914         #
1915         # enable the FIB walk process to converge the FIB
1916         #
1917         self.vapi.ppcli("test fib-walk-process enable")
1918
1919         #
1920         # packets should still be forwarded through the remaining peer
1921         #
1922         self.pg0.add_stream(pkts)
1923         self.pg_enable_capture(self.pg_interfaces)
1924         self.pg_start()
1925
1926         rx0 = self.pg3.get_capture(NUM_PKTS)
1927         self.assertEqual(len(pkts), len(rx0),
1928                          "Expected all (%s) packets across single path. "
1929                          "rx0: %s." % (len(pkts), len(rx0)))
1930
1931         #
1932         # put the connected routes back
1933         #
1934         self.pg2.config_ip4()
1935         self.pg2.resolve_arp()
1936
1937         self.pg0.add_stream(pkts)
1938         self.pg_enable_capture(self.pg_interfaces)
1939         self.pg_start()
1940
1941         rx0 = self.pg2._get_capture(NUM_PKTS)
1942         rx1 = self.pg3._get_capture(NUM_PKTS)
1943         self.assertNotEqual(0, len(rx0))
1944         self.assertNotEqual(0, len(rx1))
1945         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1946                          "Expected all (%s) packets across both ECMP paths. "
1947                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1948
1949     def test_mpls_v6_ebgp_pic(self):
1950         """ MPLSv6 eBGP Prefix-Independent Convergence (PIC) edge convergence
1951
1952         1) setup many eBGP VPNv6 routes via a pair of eBGP peers
1953         2) Check EMCP forwarding to these peers
1954         3) withdraw one eBGP path - expect LB across remaining eBGP
1955         """
1956
1957         #
1958         # Lot's of VPN routes. We need more the 64 so VPP will build
1959         # the fast convergence indirection
1960         #
1961         vpn_routes = []
1962         vpn_bindings = []
1963         pkts = []
1964         for ii in range(NUM_PKTS):
1965             dst = "3000::%d" % ii
1966             local_label = 1600 + ii
1967             vpn_routes.append(VppIpRoute(
1968                 self, dst, 128,
1969                 [VppRoutePath(
1970                     self.pg2.remote_ip6,
1971                     0xffffffff,
1972                     nh_table_id=1,
1973                     flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED),
1974                  VppRoutePath(
1975                      self.pg3.remote_ip6,
1976                      0xffffffff,
1977                      nh_table_id=1,
1978                      flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED)],
1979                 table_id=1))
1980             vpn_routes[ii].add_vpp_config()
1981
1982             vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 128,
1983                                               ip_table_id=1))
1984             vpn_bindings[ii].add_vpp_config()
1985
1986             pkts.append(Ether(dst=self.pg0.local_mac,
1987                               src=self.pg0.remote_mac) /
1988                         MPLS(label=local_label, ttl=64) /
1989                         IPv6(src=self.pg0.remote_ip6, dst=dst) /
1990                         UDP(sport=1234, dport=1234) /
1991                         Raw(b'\xa5' * 100))
1992             self.logger.info(self.vapi.cli("sh ip6 fib %s" % dst))
1993
1994         self.pg0.add_stream(pkts)
1995         self.pg_enable_capture(self.pg_interfaces)
1996         self.pg_start()
1997
1998         rx0 = self.pg2._get_capture(NUM_PKTS)
1999         rx1 = self.pg3._get_capture(NUM_PKTS)
2000         self.assertNotEqual(0, len(rx0))
2001         self.assertNotEqual(0, len(rx1))
2002         self.assertEqual(len(pkts), len(rx0) + len(rx1),
2003                          "Expected all (%s) packets across both ECMP paths. "
2004                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
2005
2006         #
2007         # use a test CLI command to stop the FIB walk process, this
2008         # will prevent the FIB converging the VPN routes and thus allow
2009         # us to probe the interim (post-fail, pre-converge) state
2010         #
2011         self.vapi.ppcli("test fib-walk-process disable")
2012
2013         #
2014         # withdraw the connected prefix on the interface.
2015         # and shutdown the interface so the ND cache is flushed.
2016         #
2017         self.pg2.unconfig_ip6()
2018         self.pg2.admin_down()
2019
2020         #
2021         # now all packets should be forwarded through the remaining peer
2022         #
2023         self.pg0.add_stream(pkts)
2024         self.pg_enable_capture(self.pg_interfaces)
2025         self.pg_start()
2026
2027         rx0 = self.pg3.get_capture(NUM_PKTS)
2028         self.assertEqual(len(pkts), len(rx0),
2029                          "Expected all (%s) packets across single path. "
2030                          "rx0: %s." % (len(pkts), len(rx0)))
2031
2032         #
2033         # enable the FIB walk process to converge the FIB
2034         #
2035         self.vapi.ppcli("test fib-walk-process enable")
2036         self.pg0.add_stream(pkts)
2037         self.pg_enable_capture(self.pg_interfaces)
2038         self.pg_start()
2039
2040         rx0 = self.pg3.get_capture(NUM_PKTS)
2041         self.assertEqual(len(pkts), len(rx0),
2042                          "Expected all (%s) packets across single path. "
2043                          "rx0: %s." % (len(pkts), len(rx0)))
2044
2045         #
2046         # put the connected routes back
2047         #
2048         self.pg2.admin_up()
2049         self.pg2.config_ip6()
2050         self.pg2.resolve_ndp()
2051
2052         self.pg0.add_stream(pkts)
2053         self.pg_enable_capture(self.pg_interfaces)
2054         self.pg_start()
2055
2056         rx0 = self.pg2._get_capture(NUM_PKTS)
2057         rx1 = self.pg3._get_capture(NUM_PKTS)
2058         self.assertNotEqual(0, len(rx0))
2059         self.assertNotEqual(0, len(rx1))
2060         self.assertEqual(len(pkts), len(rx0) + len(rx1),
2061                          "Expected all (%s) packets across both ECMP paths. "
2062                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
2063
2064
2065 class TestMPLSL2(VppTestCase):
2066     """ MPLS-L2 """
2067
2068     @classmethod
2069     def setUpClass(cls):
2070         super(TestMPLSL2, cls).setUpClass()
2071
2072     @classmethod
2073     def tearDownClass(cls):
2074         super(TestMPLSL2, cls).tearDownClass()
2075
2076     def setUp(self):
2077         super(TestMPLSL2, self).setUp()
2078
2079         # create 2 pg interfaces
2080         self.create_pg_interfaces(range(2))
2081
2082         # create the default MPLS table
2083         self.tables = []
2084         tbl = VppMplsTable(self, 0)
2085         tbl.add_vpp_config()
2086         self.tables.append(tbl)
2087
2088         # use pg0 as the core facing interface, don't resolve ARP
2089         self.pg0.admin_up()
2090         self.pg0.config_ip4()
2091         self.pg0.enable_mpls()
2092
2093         # use the other 2 for customer facing L2 links
2094         for i in self.pg_interfaces[1:]:
2095             i.admin_up()
2096
2097     def tearDown(self):
2098         for i in self.pg_interfaces[1:]:
2099             i.admin_down()
2100
2101         self.pg0.disable_mpls()
2102         self.pg0.unconfig_ip4()
2103         self.pg0.admin_down()
2104         super(TestMPLSL2, self).tearDown()
2105
2106     def verify_capture_tunneled_ethernet(self, capture, sent, mpls_labels):
2107         capture = verify_filter(capture, sent)
2108
2109         self.assertEqual(len(capture), len(sent))
2110
2111         for i in range(len(capture)):
2112             tx = sent[i]
2113             rx = capture[i]
2114
2115             # the MPLS TTL is 255 since it enters a new tunnel
2116             verify_mpls_stack(self, rx, mpls_labels)
2117
2118             tx_eth = tx[Ether]
2119             rx_eth = Ether(scapy.compat.raw(rx[MPLS].payload))
2120
2121             self.assertEqual(rx_eth.src, tx_eth.src)
2122             self.assertEqual(rx_eth.dst, tx_eth.dst)
2123
2124     def verify_arp_req(self, rx, smac, sip, dip):
2125         ether = rx[Ether]
2126         self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
2127         self.assertEqual(ether.src, smac)
2128
2129         arp = rx[ARP]
2130         self.assertEqual(arp.hwtype, 1)
2131         self.assertEqual(arp.ptype, 0x800)
2132         self.assertEqual(arp.hwlen, 6)
2133         self.assertEqual(arp.plen, 4)
2134         self.assertEqual(arp.op, ARP.who_has)
2135         self.assertEqual(arp.hwsrc, smac)
2136         self.assertEqual(arp.hwdst, "00:00:00:00:00:00")
2137         self.assertEqual(arp.psrc, sip)
2138         self.assertEqual(arp.pdst, dip)
2139
2140     def test_vpws(self):
2141         """ Virtual Private Wire Service """
2142
2143         #
2144         # Create an MPLS tunnel that pushes 1 label
2145         # For Ethernet over MPLS the uniform mode is irrelevant since ttl/cos
2146         # information is not in the packet, but we test it works anyway
2147         #
2148         mpls_tun_1 = VppMPLSTunnelInterface(
2149             self,
2150             [VppRoutePath(self.pg0.remote_ip4,
2151                           self.pg0.sw_if_index,
2152                           labels=[VppMplsLabel(42, MplsLspMode.UNIFORM)])],
2153             is_l2=1)
2154         mpls_tun_1.add_vpp_config()
2155         mpls_tun_1.admin_up()
2156
2157         #
2158         # Create a label entry to for 55 that does L2 input to the tunnel
2159         #
2160         route_55_eos = VppMplsRoute(
2161             self, 55, 1,
2162             [VppRoutePath("0.0.0.0",
2163                           mpls_tun_1.sw_if_index,
2164                           type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2165                           proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)],
2166             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)
2167         route_55_eos.add_vpp_config()
2168
2169         #
2170         # Cross-connect the tunnel with one of the customers L2 interfaces
2171         #
2172         self.vapi.sw_interface_set_l2_xconnect(self.pg1.sw_if_index,
2173                                                mpls_tun_1.sw_if_index,
2174                                                enable=1)
2175         self.vapi.sw_interface_set_l2_xconnect(mpls_tun_1.sw_if_index,
2176                                                self.pg1.sw_if_index,
2177                                                enable=1)
2178
2179         #
2180         # inject a packet from the core
2181         #
2182         pcore = (Ether(dst=self.pg0.local_mac,
2183                        src=self.pg0.remote_mac) /
2184                  MPLS(label=55, ttl=64) /
2185                  Ether(dst="00:00:de:ad:ba:be",
2186                        src="00:00:de:ad:be:ef") /
2187                  IP(src="10.10.10.10", dst="11.11.11.11") /
2188                  UDP(sport=1234, dport=1234) /
2189                  Raw(b'\xa5' * 100))
2190
2191         tx0 = pcore * NUM_PKTS
2192         rx0 = self.send_and_expect(self.pg0, tx0, self.pg1)
2193         payload = pcore[MPLS].payload
2194
2195         self.assertEqual(rx0[0][Ether].dst, payload[Ether].dst)
2196         self.assertEqual(rx0[0][Ether].src, payload[Ether].src)
2197
2198         #
2199         # Inject a packet from the customer/L2 side
2200         # there's no resolved ARP entry so the first packet we see should be
2201         # an ARP request
2202         #
2203         tx1 = pcore[MPLS].payload
2204         rx1 = self.send_and_expect(self.pg1, [tx1], self.pg0)
2205
2206         self.verify_arp_req(rx1[0],
2207                             self.pg0.local_mac,
2208                             self.pg0.local_ip4,
2209                             self.pg0.remote_ip4)
2210
2211         #
2212         # resolve the ARP entries and send again
2213         #
2214         self.pg0.resolve_arp()
2215         tx1 = pcore[MPLS].payload * NUM_PKTS
2216         rx1 = self.send_and_expect(self.pg1, tx1, self.pg0)
2217
2218         self.verify_capture_tunneled_ethernet(rx1, tx1, [VppMplsLabel(42)])
2219
2220     def test_vpls(self):
2221         """ Virtual Private LAN Service """
2222
2223         # we skipped this in the setup
2224         self.pg0.resolve_arp()
2225
2226         #
2227         # Create a L2 MPLS tunnels
2228         #
2229         mpls_tun1 = VppMPLSTunnelInterface(
2230             self,
2231             [VppRoutePath(self.pg0.remote_ip4,
2232                           self.pg0.sw_if_index,
2233                           labels=[VppMplsLabel(42)])],
2234             is_l2=1)
2235         mpls_tun1.add_vpp_config()
2236         mpls_tun1.admin_up()
2237
2238         mpls_tun2 = VppMPLSTunnelInterface(
2239             self,
2240             [VppRoutePath(self.pg0.remote_ip4,
2241                           self.pg0.sw_if_index,
2242                           labels=[VppMplsLabel(43)])],
2243             is_l2=1)
2244         mpls_tun2.add_vpp_config()
2245         mpls_tun2.admin_up()
2246
2247         #
2248         # Create a label entries, 55 and 56, that do L2 input to the tunnel
2249         # the latter includes a Psuedo Wire Control Word
2250         #
2251         route_55_eos = VppMplsRoute(
2252             self, 55, 1,
2253             [VppRoutePath("0.0.0.0",
2254                           mpls_tun1.sw_if_index,
2255                           type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2256                           proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)],
2257             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)
2258
2259         route_56_eos = VppMplsRoute(
2260             self, 56, 1,
2261             [VppRoutePath("0.0.0.0",
2262                           mpls_tun2.sw_if_index,
2263                           type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2264                           flags=FibPathFlags.FIB_PATH_FLAG_POP_PW_CW,
2265                           proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)],
2266             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)
2267
2268         # move me
2269         route_56_eos.add_vpp_config()
2270         route_55_eos.add_vpp_config()
2271
2272         self.logger.info(self.vapi.cli("sh mpls fib 56"))
2273
2274         #
2275         # add to tunnel to the customers bridge-domain
2276         #
2277         self.vapi.sw_interface_set_l2_bridge(
2278             rx_sw_if_index=mpls_tun1.sw_if_index, bd_id=1)
2279         self.vapi.sw_interface_set_l2_bridge(
2280             rx_sw_if_index=mpls_tun2.sw_if_index, bd_id=1)
2281         self.vapi.sw_interface_set_l2_bridge(
2282             rx_sw_if_index=self.pg1.sw_if_index, bd_id=1)
2283
2284         #
2285         # Packet from host on the customer interface to each host
2286         # reachable over the core, and vice-versa
2287         #
2288         p_cust1 = (Ether(dst="00:00:de:ad:ba:b1",
2289                          src="00:00:de:ad:be:ef") /
2290                    IP(src="10.10.10.10", dst="11.11.11.11") /
2291                    UDP(sport=1234, dport=1234) /
2292                    Raw(b'\xa5' * 100))
2293         p_cust2 = (Ether(dst="00:00:de:ad:ba:b2",
2294                          src="00:00:de:ad:be:ef") /
2295                    IP(src="10.10.10.10", dst="11.11.11.12") /
2296                    UDP(sport=1234, dport=1234) /
2297                    Raw(b'\xa5' * 100))
2298         p_core1 = (Ether(dst=self.pg0.local_mac,
2299                          src=self.pg0.remote_mac) /
2300                    MPLS(label=55, ttl=64) /
2301                    Ether(src="00:00:de:ad:ba:b1",
2302                          dst="00:00:de:ad:be:ef") /
2303                    IP(dst="10.10.10.10", src="11.11.11.11") /
2304                    UDP(sport=1234, dport=1234) /
2305                    Raw(b'\xa5' * 100))
2306         p_core2 = (Ether(dst=self.pg0.local_mac,
2307                          src=self.pg0.remote_mac) /
2308                    MPLS(label=56, ttl=64) /
2309                    Raw(b'\x01' * 4) /  # PW CW
2310                    Ether(src="00:00:de:ad:ba:b2",
2311                          dst="00:00:de:ad:be:ef") /
2312                    IP(dst="10.10.10.10", src="11.11.11.12") /
2313                    UDP(sport=1234, dport=1234) /
2314                    Raw(b'\xa5' * 100))
2315
2316         #
2317         # The BD is learning, so send in one of each packet to learn
2318         #
2319
2320         # 2 packets due to BD flooding
2321         rx = self.send_and_expect(self.pg1, p_cust1, self.pg0, n_rx=2)
2322         rx = self.send_and_expect(self.pg1, p_cust2, self.pg0, n_rx=2)
2323
2324         # we've learnt this so expect it be be forwarded not flooded
2325         rx = self.send_and_expect(self.pg0, [p_core1], self.pg1)
2326         self.assertEqual(rx[0][Ether].dst, p_cust1[Ether].src)
2327         self.assertEqual(rx[0][Ether].src, p_cust1[Ether].dst)
2328
2329         rx = self.send_and_expect(self.pg0, [p_core2], self.pg1)
2330         self.assertEqual(rx[0][Ether].dst, p_cust2[Ether].src)
2331         self.assertEqual(rx[0][Ether].src, p_cust2[Ether].dst)
2332
2333         #
2334         # now a stream in each direction from each host
2335         #
2336         rx = self.send_and_expect(self.pg1, p_cust1 * NUM_PKTS, self.pg0)
2337         self.verify_capture_tunneled_ethernet(rx, p_cust1 * NUM_PKTS,
2338                                               [VppMplsLabel(42)])
2339
2340         rx = self.send_and_expect(self.pg1, p_cust2 * NUM_PKTS, self.pg0)
2341         self.verify_capture_tunneled_ethernet(rx, p_cust2 * NUM_PKTS,
2342                                               [VppMplsLabel(43)])
2343
2344         rx = self.send_and_expect(self.pg0, p_core1 * NUM_PKTS, self.pg1)
2345         rx = self.send_and_expect(self.pg0, p_core2 * NUM_PKTS, self.pg1)
2346
2347         #
2348         # remove interfaces from customers bridge-domain
2349         #
2350         self.vapi.sw_interface_set_l2_bridge(
2351             rx_sw_if_index=mpls_tun1.sw_if_index, bd_id=1, enable=0)
2352         self.vapi.sw_interface_set_l2_bridge(
2353             rx_sw_if_index=mpls_tun2.sw_if_index, bd_id=1, enable=0)
2354         self.vapi.sw_interface_set_l2_bridge(
2355             rx_sw_if_index=self.pg1.sw_if_index, bd_id=1, enable=0)
2356
2357
2358 if __name__ == '__main__':
2359     unittest.main(testRunner=VppTestRunner)