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