fib: fib api updates
[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
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
1398 class TestMPLSDisabled(VppTestCase):
1399     """ MPLS disabled """
1400
1401     @classmethod
1402     def setUpClass(cls):
1403         super(TestMPLSDisabled, cls).setUpClass()
1404
1405     @classmethod
1406     def tearDownClass(cls):
1407         super(TestMPLSDisabled, cls).tearDownClass()
1408
1409     def setUp(self):
1410         super(TestMPLSDisabled, self).setUp()
1411
1412         # create 2 pg interfaces
1413         self.create_pg_interfaces(range(2))
1414
1415         self.tbl = VppMplsTable(self, 0)
1416         self.tbl.add_vpp_config()
1417
1418         # PG0 is MPLS enabled
1419         self.pg0.admin_up()
1420         self.pg0.config_ip4()
1421         self.pg0.resolve_arp()
1422         self.pg0.enable_mpls()
1423
1424         # PG 1 is not MPLS enabled
1425         self.pg1.admin_up()
1426
1427     def tearDown(self):
1428         for i in self.pg_interfaces:
1429             i.unconfig_ip4()
1430             i.admin_down()
1431
1432         self.pg0.disable_mpls()
1433         super(TestMPLSDisabled, self).tearDown()
1434
1435     def test_mpls_disabled(self):
1436         """ MPLS Disabled """
1437
1438         tx = (Ether(src=self.pg1.remote_mac,
1439                     dst=self.pg1.local_mac) /
1440               MPLS(label=32, ttl=64) /
1441               IPv6(src="2001::1", dst=self.pg0.remote_ip6) /
1442               UDP(sport=1234, dport=1234) /
1443               Raw('\xa5' * 100))
1444
1445         #
1446         # A simple MPLS xconnect - eos label in label out
1447         #
1448         route_32_eos = VppMplsRoute(self, 32, 1,
1449                                     [VppRoutePath(self.pg0.remote_ip4,
1450                                                   self.pg0.sw_if_index,
1451                                                   labels=[33])])
1452         route_32_eos.add_vpp_config()
1453
1454         #
1455         # PG1 does not forward IP traffic
1456         #
1457         self.send_and_assert_no_replies(self.pg1, tx, "MPLS disabled")
1458
1459         #
1460         # MPLS enable PG1
1461         #
1462         self.pg1.enable_mpls()
1463
1464         #
1465         # Now we get packets through
1466         #
1467         self.pg1.add_stream(tx)
1468         self.pg_enable_capture(self.pg_interfaces)
1469         self.pg_start()
1470
1471         rx = self.pg0.get_capture(1)
1472
1473         #
1474         # Disable PG1
1475         #
1476         self.pg1.disable_mpls()
1477
1478         #
1479         # PG1 does not forward IP traffic
1480         #
1481         self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1482         self.send_and_assert_no_replies(self.pg1, tx, "IPv6 disabled")
1483
1484
1485 class TestMPLSPIC(VppTestCase):
1486     """ MPLS Prefix-Independent Convergence (PIC) edge convergence """
1487
1488     @classmethod
1489     def setUpClass(cls):
1490         super(TestMPLSPIC, cls).setUpClass()
1491
1492     @classmethod
1493     def tearDownClass(cls):
1494         super(TestMPLSPIC, cls).tearDownClass()
1495
1496     def setUp(self):
1497         super(TestMPLSPIC, self).setUp()
1498
1499         # create 2 pg interfaces
1500         self.create_pg_interfaces(range(4))
1501
1502         mpls_tbl = VppMplsTable(self, 0)
1503         mpls_tbl.add_vpp_config()
1504         tbl4 = VppIpTable(self, 1)
1505         tbl4.add_vpp_config()
1506         tbl6 = VppIpTable(self, 1, is_ip6=1)
1507         tbl6.add_vpp_config()
1508
1509         # core links
1510         self.pg0.admin_up()
1511         self.pg0.config_ip4()
1512         self.pg0.resolve_arp()
1513         self.pg0.enable_mpls()
1514
1515         self.pg1.admin_up()
1516         self.pg1.config_ip4()
1517         self.pg1.resolve_arp()
1518         self.pg1.enable_mpls()
1519
1520         # VRF (customer facing) link
1521         self.pg2.admin_up()
1522         self.pg2.set_table_ip4(1)
1523         self.pg2.config_ip4()
1524         self.pg2.resolve_arp()
1525         self.pg2.set_table_ip6(1)
1526         self.pg2.config_ip6()
1527         self.pg2.resolve_ndp()
1528
1529         self.pg3.admin_up()
1530         self.pg3.set_table_ip4(1)
1531         self.pg3.config_ip4()
1532         self.pg3.resolve_arp()
1533         self.pg3.set_table_ip6(1)
1534         self.pg3.config_ip6()
1535         self.pg3.resolve_ndp()
1536
1537     def tearDown(self):
1538         self.pg0.disable_mpls()
1539         self.pg1.disable_mpls()
1540         for i in self.pg_interfaces:
1541             i.unconfig_ip4()
1542             i.unconfig_ip6()
1543             i.set_table_ip4(0)
1544             i.set_table_ip6(0)
1545             i.admin_down()
1546         super(TestMPLSPIC, self).tearDown()
1547
1548     def test_mpls_ibgp_pic(self):
1549         """ MPLS iBGP Prefix-Independent Convergence (PIC) edge convergence
1550
1551         1) setup many iBGP VPN routes via a pair of iBGP peers.
1552         2) Check EMCP forwarding to these peers
1553         3) withdraw the IGP route to one of these peers.
1554         4) check forwarding continues to the remaining peer
1555         """
1556
1557         #
1558         # IGP+LDP core routes
1559         #
1560         core_10_0_0_45 = VppIpRoute(self, "10.0.0.45", 32,
1561                                     [VppRoutePath(self.pg0.remote_ip4,
1562                                                   self.pg0.sw_if_index,
1563                                                   labels=[45])])
1564         core_10_0_0_45.add_vpp_config()
1565
1566         core_10_0_0_46 = VppIpRoute(self, "10.0.0.46", 32,
1567                                     [VppRoutePath(self.pg1.remote_ip4,
1568                                                   self.pg1.sw_if_index,
1569                                                   labels=[46])])
1570         core_10_0_0_46.add_vpp_config()
1571
1572         #
1573         # Lot's of VPN routes. We need more the 64 so VPP will build
1574         # the fast convergence indirection
1575         #
1576         vpn_routes = []
1577         pkts = []
1578         for ii in range(NUM_PKTS):
1579             dst = "192.168.1.%d" % ii
1580             vpn_routes.append(VppIpRoute(
1581                 self, dst, 32,
1582                 [VppRoutePath(
1583                     "10.0.0.45",
1584                     0xffffffff,
1585                     labels=[145],
1586                     flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_HOST),
1587                  VppRoutePath(
1588                      "10.0.0.46",
1589                      0xffffffff,
1590                      labels=[146],
1591                      flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_HOST)],
1592                 table_id=1))
1593             vpn_routes[ii].add_vpp_config()
1594
1595             pkts.append(Ether(dst=self.pg2.local_mac,
1596                               src=self.pg2.remote_mac) /
1597                         IP(src=self.pg2.remote_ip4, dst=dst) /
1598                         UDP(sport=1234, dport=1234) /
1599                         Raw('\xa5' * 100))
1600
1601         #
1602         # Send the packet stream (one pkt to each VPN route)
1603         #  - expect a 50-50 split of the traffic
1604         #
1605         self.pg2.add_stream(pkts)
1606         self.pg_enable_capture(self.pg_interfaces)
1607         self.pg_start()
1608
1609         rx0 = self.pg0._get_capture(NUM_PKTS)
1610         rx1 = self.pg1._get_capture(NUM_PKTS)
1611
1612         # not testing the LB hashing algorithm so we're not concerned
1613         # with the split ratio, just as long as neither is 0
1614         self.assertNotEqual(0, len(rx0))
1615         self.assertNotEqual(0, len(rx1))
1616         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1617                          "Expected all (%s) packets across both ECMP paths. "
1618                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1619
1620         #
1621         # use a test CLI command to stop the FIB walk process, this
1622         # will prevent the FIB converging the VPN routes and thus allow
1623         # us to probe the interim (post-fail, pre-converge) state
1624         #
1625         self.vapi.ppcli("test fib-walk-process disable")
1626
1627         #
1628         # Withdraw one of the IGP routes
1629         #
1630         core_10_0_0_46.remove_vpp_config()
1631
1632         #
1633         # now all packets should be forwarded through the remaining peer
1634         #
1635         self.vapi.ppcli("clear trace")
1636         self.pg2.add_stream(pkts)
1637         self.pg_enable_capture(self.pg_interfaces)
1638         self.pg_start()
1639
1640         rx0 = self.pg0.get_capture(NUM_PKTS)
1641         self.assertEqual(len(pkts), len(rx0),
1642                          "Expected all (%s) packets across single path. "
1643                          "rx0: %s." % (len(pkts), len(rx0)))
1644
1645         #
1646         # enable the FIB walk process to converge the FIB
1647         #
1648         self.vapi.ppcli("test fib-walk-process enable")
1649
1650         #
1651         # packets should still be forwarded through the remaining peer
1652         #
1653         self.pg2.add_stream(pkts)
1654         self.pg_enable_capture(self.pg_interfaces)
1655         self.pg_start()
1656
1657         rx0 = self.pg0.get_capture(NUM_PKTS)
1658         self.assertEqual(len(pkts), len(rx0),
1659                          "Expected all (%s) packets across single path. "
1660                          "rx0: %s." % (len(pkts), len(rx0)))
1661
1662         #
1663         # Add the IGP route back and we return to load-balancing
1664         #
1665         core_10_0_0_46.add_vpp_config()
1666
1667         self.pg2.add_stream(pkts)
1668         self.pg_enable_capture(self.pg_interfaces)
1669         self.pg_start()
1670
1671         rx0 = self.pg0._get_capture(NUM_PKTS)
1672         rx1 = self.pg1._get_capture(NUM_PKTS)
1673         self.assertNotEqual(0, len(rx0))
1674         self.assertNotEqual(0, len(rx1))
1675         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1676                          "Expected all (%s) packets across both ECMP paths. "
1677                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1678
1679     def test_mpls_ebgp_pic(self):
1680         """ MPLS eBGP Prefix-Independent Convergence (PIC) edge convergence
1681
1682         1) setup many eBGP VPN routes via a pair of eBGP peers.
1683         2) Check EMCP forwarding to these peers
1684         3) withdraw one eBGP path - expect LB across remaining eBGP
1685         """
1686
1687         #
1688         # Lot's of VPN routes. We need more the 64 so VPP will build
1689         # the fast convergence indirection
1690         #
1691         vpn_routes = []
1692         vpn_bindings = []
1693         pkts = []
1694         for ii in range(NUM_PKTS):
1695             dst = "192.168.1.%d" % ii
1696             local_label = 1600 + ii
1697             vpn_routes.append(VppIpRoute(
1698                 self, dst, 32,
1699                 [VppRoutePath(
1700                     self.pg2.remote_ip4,
1701                     0xffffffff,
1702                     nh_table_id=1,
1703                     flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED),
1704                  VppRoutePath(
1705                      self.pg3.remote_ip4,
1706                      0xffffffff,
1707                      nh_table_id=1,
1708                      flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED)],
1709                 table_id=1))
1710             vpn_routes[ii].add_vpp_config()
1711
1712             vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 32,
1713                                               ip_table_id=1))
1714             vpn_bindings[ii].add_vpp_config()
1715
1716             pkts.append(Ether(dst=self.pg0.local_mac,
1717                               src=self.pg0.remote_mac) /
1718                         MPLS(label=local_label, ttl=64) /
1719                         IP(src=self.pg0.remote_ip4, dst=dst) /
1720                         UDP(sport=1234, dport=1234) /
1721                         Raw('\xa5' * 100))
1722
1723         #
1724         # Send the packet stream (one pkt to each VPN route)
1725         #  - expect a 50-50 split of the traffic
1726         #
1727         self.pg0.add_stream(pkts)
1728         self.pg_enable_capture(self.pg_interfaces)
1729         self.pg_start()
1730
1731         rx0 = self.pg2._get_capture(NUM_PKTS)
1732         rx1 = self.pg3._get_capture(NUM_PKTS)
1733
1734         # not testing the LB hashing algorithm so we're not concerned
1735         # with the split ratio, just as long as neither is 0
1736         self.assertNotEqual(0, len(rx0))
1737         self.assertNotEqual(0, len(rx1))
1738         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1739                          "Expected all (%s) packets across both ECMP paths. "
1740                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1741
1742         #
1743         # use a test CLI command to stop the FIB walk process, this
1744         # will prevent the FIB converging the VPN routes and thus allow
1745         # us to probe the interim (post-fail, pre-converge) state
1746         #
1747         self.vapi.ppcli("test fib-walk-process disable")
1748
1749         #
1750         # withdraw the connected prefix on the interface.
1751         #
1752         self.pg2.unconfig_ip4()
1753
1754         #
1755         # now all packets should be forwarded through the remaining peer
1756         #
1757         self.pg0.add_stream(pkts)
1758         self.pg_enable_capture(self.pg_interfaces)
1759         self.pg_start()
1760
1761         rx0 = self.pg3.get_capture(NUM_PKTS)
1762         self.assertEqual(len(pkts), len(rx0),
1763                          "Expected all (%s) packets across single path. "
1764                          "rx0: %s." % (len(pkts), len(rx0)))
1765
1766         #
1767         # enable the FIB walk process to converge the FIB
1768         #
1769         self.vapi.ppcli("test fib-walk-process enable")
1770
1771         #
1772         # packets should still be forwarded through the remaining peer
1773         #
1774         self.pg0.add_stream(pkts)
1775         self.pg_enable_capture(self.pg_interfaces)
1776         self.pg_start()
1777
1778         rx0 = self.pg3.get_capture(NUM_PKTS)
1779         self.assertEqual(len(pkts), len(rx0),
1780                          "Expected all (%s) packets across single path. "
1781                          "rx0: %s." % (len(pkts), len(rx0)))
1782
1783         #
1784         # put the connected routes back
1785         #
1786         self.pg2.config_ip4()
1787         self.pg2.resolve_arp()
1788
1789         self.pg0.add_stream(pkts)
1790         self.pg_enable_capture(self.pg_interfaces)
1791         self.pg_start()
1792
1793         rx0 = self.pg2._get_capture(NUM_PKTS)
1794         rx1 = self.pg3._get_capture(NUM_PKTS)
1795         self.assertNotEqual(0, len(rx0))
1796         self.assertNotEqual(0, len(rx1))
1797         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1798                          "Expected all (%s) packets across both ECMP paths. "
1799                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1800
1801     def test_mpls_v6_ebgp_pic(self):
1802         """ MPLSv6 eBGP Prefix-Independent Convergence (PIC) edge convergence
1803
1804         1) setup many eBGP VPNv6 routes via a pair of eBGP peers
1805         2) Check EMCP forwarding to these peers
1806         3) withdraw one eBGP path - expect LB across remaining eBGP
1807         """
1808
1809         #
1810         # Lot's of VPN routes. We need more the 64 so VPP will build
1811         # the fast convergence indirection
1812         #
1813         vpn_routes = []
1814         vpn_bindings = []
1815         pkts = []
1816         for ii in range(NUM_PKTS):
1817             dst = "3000::%d" % ii
1818             local_label = 1600 + ii
1819             vpn_routes.append(VppIpRoute(
1820                 self, dst, 128,
1821                 [VppRoutePath(
1822                     self.pg2.remote_ip6,
1823                     0xffffffff,
1824                     nh_table_id=1,
1825                     flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED),
1826                  VppRoutePath(
1827                      self.pg3.remote_ip6,
1828                      0xffffffff,
1829                      nh_table_id=1,
1830                      flags=FibPathFlags.FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED)],
1831                 table_id=1))
1832             vpn_routes[ii].add_vpp_config()
1833
1834             vpn_bindings.append(VppMplsIpBind(self, local_label, dst, 128,
1835                                               ip_table_id=1))
1836             vpn_bindings[ii].add_vpp_config()
1837
1838             pkts.append(Ether(dst=self.pg0.local_mac,
1839                               src=self.pg0.remote_mac) /
1840                         MPLS(label=local_label, ttl=64) /
1841                         IPv6(src=self.pg0.remote_ip6, dst=dst) /
1842                         UDP(sport=1234, dport=1234) /
1843                         Raw('\xa5' * 100))
1844             self.logger.info(self.vapi.cli("sh ip6 fib %s" % dst))
1845
1846         self.pg0.add_stream(pkts)
1847         self.pg_enable_capture(self.pg_interfaces)
1848         self.pg_start()
1849
1850         rx0 = self.pg2._get_capture(NUM_PKTS)
1851         rx1 = self.pg3._get_capture(NUM_PKTS)
1852         self.assertNotEqual(0, len(rx0))
1853         self.assertNotEqual(0, len(rx1))
1854         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1855                          "Expected all (%s) packets across both ECMP paths. "
1856                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1857
1858         #
1859         # use a test CLI command to stop the FIB walk process, this
1860         # will prevent the FIB converging the VPN routes and thus allow
1861         # us to probe the interim (post-fail, pre-converge) state
1862         #
1863         self.vapi.ppcli("test fib-walk-process disable")
1864
1865         #
1866         # withdraw the connected prefix on the interface.
1867         # and shutdown the interface so the ND cache is flushed.
1868         #
1869         self.pg2.unconfig_ip6()
1870         self.pg2.admin_down()
1871
1872         #
1873         # now all packets should be forwarded through the remaining peer
1874         #
1875         self.pg0.add_stream(pkts)
1876         self.pg_enable_capture(self.pg_interfaces)
1877         self.pg_start()
1878
1879         rx0 = self.pg3.get_capture(NUM_PKTS)
1880         self.assertEqual(len(pkts), len(rx0),
1881                          "Expected all (%s) packets across single path. "
1882                          "rx0: %s." % (len(pkts), len(rx0)))
1883
1884         #
1885         # enable the FIB walk process to converge the FIB
1886         #
1887         self.vapi.ppcli("test fib-walk-process enable")
1888         self.pg0.add_stream(pkts)
1889         self.pg_enable_capture(self.pg_interfaces)
1890         self.pg_start()
1891
1892         rx0 = self.pg3.get_capture(NUM_PKTS)
1893         self.assertEqual(len(pkts), len(rx0),
1894                          "Expected all (%s) packets across single path. "
1895                          "rx0: %s." % (len(pkts), len(rx0)))
1896
1897         #
1898         # put the connected routes back
1899         #
1900         self.pg2.admin_up()
1901         self.pg2.config_ip6()
1902         self.pg2.resolve_ndp()
1903
1904         self.pg0.add_stream(pkts)
1905         self.pg_enable_capture(self.pg_interfaces)
1906         self.pg_start()
1907
1908         rx0 = self.pg2._get_capture(NUM_PKTS)
1909         rx1 = self.pg3._get_capture(NUM_PKTS)
1910         self.assertNotEqual(0, len(rx0))
1911         self.assertNotEqual(0, len(rx1))
1912         self.assertEqual(len(pkts), len(rx0) + len(rx1),
1913                          "Expected all (%s) packets across both ECMP paths. "
1914                          "rx0: %s rx1: %s." % (len(pkts), len(rx0), len(rx1)))
1915
1916
1917 class TestMPLSL2(VppTestCase):
1918     """ MPLS-L2 """
1919
1920     @classmethod
1921     def setUpClass(cls):
1922         super(TestMPLSL2, cls).setUpClass()
1923
1924     @classmethod
1925     def tearDownClass(cls):
1926         super(TestMPLSL2, cls).tearDownClass()
1927
1928     def setUp(self):
1929         super(TestMPLSL2, self).setUp()
1930
1931         # create 2 pg interfaces
1932         self.create_pg_interfaces(range(2))
1933
1934         # create the default MPLS table
1935         self.tables = []
1936         tbl = VppMplsTable(self, 0)
1937         tbl.add_vpp_config()
1938         self.tables.append(tbl)
1939
1940         # use pg0 as the core facing interface
1941         self.pg0.admin_up()
1942         self.pg0.config_ip4()
1943         self.pg0.resolve_arp()
1944         self.pg0.enable_mpls()
1945
1946         # use the other 2 for customer facing L2 links
1947         for i in self.pg_interfaces[1:]:
1948             i.admin_up()
1949
1950     def tearDown(self):
1951         for i in self.pg_interfaces[1:]:
1952             i.admin_down()
1953
1954         self.pg0.disable_mpls()
1955         self.pg0.unconfig_ip4()
1956         self.pg0.admin_down()
1957         super(TestMPLSL2, self).tearDown()
1958
1959     def verify_capture_tunneled_ethernet(self, capture, sent, mpls_labels):
1960         capture = verify_filter(capture, sent)
1961
1962         self.assertEqual(len(capture), len(sent))
1963
1964         for i in range(len(capture)):
1965             tx = sent[i]
1966             rx = capture[i]
1967
1968             # the MPLS TTL is 255 since it enters a new tunnel
1969             verify_mpls_stack(self, rx, mpls_labels)
1970
1971             tx_eth = tx[Ether]
1972             rx_eth = Ether(scapy.compat.raw(rx[MPLS].payload))
1973
1974             self.assertEqual(rx_eth.src, tx_eth.src)
1975             self.assertEqual(rx_eth.dst, tx_eth.dst)
1976
1977     def test_vpws(self):
1978         """ Virtual Private Wire Service """
1979
1980         #
1981         # Create an MPLS tunnel that pushes 1 label
1982         # For Ethernet over MPLS the uniform mode is irrelevant since ttl/cos
1983         # information is not in the packet, but we test it works anyway
1984         #
1985         mpls_tun_1 = VppMPLSTunnelInterface(
1986             self,
1987             [VppRoutePath(self.pg0.remote_ip4,
1988                           self.pg0.sw_if_index,
1989                           labels=[VppMplsLabel(42, MplsLspMode.UNIFORM)])],
1990             is_l2=1)
1991         mpls_tun_1.add_vpp_config()
1992         mpls_tun_1.admin_up()
1993
1994         #
1995         # Create a label entry to for 55 that does L2 input to the tunnel
1996         #
1997         route_55_eos = VppMplsRoute(
1998             self, 55, 1,
1999             [VppRoutePath("0.0.0.0",
2000                           mpls_tun_1.sw_if_index,
2001                           type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2002                           proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)],
2003             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)
2004         route_55_eos.add_vpp_config()
2005
2006         #
2007         # Cross-connect the tunnel with one of the customers L2 interfaces
2008         #
2009         self.vapi.sw_interface_set_l2_xconnect(self.pg1.sw_if_index,
2010                                                mpls_tun_1.sw_if_index,
2011                                                enable=1)
2012         self.vapi.sw_interface_set_l2_xconnect(mpls_tun_1.sw_if_index,
2013                                                self.pg1.sw_if_index,
2014                                                enable=1)
2015
2016         #
2017         # inject a packet from the core
2018         #
2019         pcore = (Ether(dst=self.pg0.local_mac,
2020                        src=self.pg0.remote_mac) /
2021                  MPLS(label=55, ttl=64) /
2022                  Ether(dst="00:00:de:ad:ba:be",
2023                        src="00:00:de:ad:be:ef") /
2024                  IP(src="10.10.10.10", dst="11.11.11.11") /
2025                  UDP(sport=1234, dport=1234) /
2026                  Raw('\xa5' * 100))
2027
2028         tx0 = pcore * NUM_PKTS
2029         rx0 = self.send_and_expect(self.pg0, tx0, self.pg1)
2030         payload = pcore[MPLS].payload
2031
2032         self.assertEqual(rx0[0][Ether].dst, payload[Ether].dst)
2033         self.assertEqual(rx0[0][Ether].src, payload[Ether].src)
2034
2035         #
2036         # Inject a packet from the customer/L2 side
2037         #
2038         tx1 = pcore[MPLS].payload * NUM_PKTS
2039         rx1 = self.send_and_expect(self.pg1, tx1, self.pg0)
2040
2041         self.verify_capture_tunneled_ethernet(rx1, tx1, [VppMplsLabel(42)])
2042
2043     def test_vpls(self):
2044         """ Virtual Private LAN Service """
2045         #
2046         # Create an L2 MPLS tunnel
2047         #
2048         mpls_tun = VppMPLSTunnelInterface(
2049             self,
2050             [VppRoutePath(self.pg0.remote_ip4,
2051                           self.pg0.sw_if_index,
2052                           labels=[VppMplsLabel(42)])],
2053             is_l2=1)
2054         mpls_tun.add_vpp_config()
2055         mpls_tun.admin_up()
2056
2057         #
2058         # Create a label entry to for 55 that does L2 input to the tunnel
2059         #
2060         route_55_eos = VppMplsRoute(
2061             self, 55, 1,
2062             [VppRoutePath("0.0.0.0",
2063                           mpls_tun.sw_if_index,
2064                           type=FibPathType.FIB_PATH_TYPE_INTERFACE_RX,
2065                           proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)],
2066             eos_proto=FibPathProto.FIB_PATH_NH_PROTO_ETHERNET)
2067         route_55_eos.add_vpp_config()
2068
2069         #
2070         # add to tunnel to the customers bridge-domain
2071         #
2072         self.vapi.sw_interface_set_l2_bridge(
2073             rx_sw_if_index=mpls_tun.sw_if_index, bd_id=1)
2074         self.vapi.sw_interface_set_l2_bridge(
2075             rx_sw_if_index=self.pg1.sw_if_index, bd_id=1)
2076
2077         #
2078         # Packet from the customer interface and from the core
2079         #
2080         p_cust = (Ether(dst="00:00:de:ad:ba:be",
2081                         src="00:00:de:ad:be:ef") /
2082                   IP(src="10.10.10.10", dst="11.11.11.11") /
2083                   UDP(sport=1234, dport=1234) /
2084                   Raw('\xa5' * 100))
2085         p_core = (Ether(src="00:00:de:ad:ba:be",
2086                         dst="00:00:de:ad:be:ef") /
2087                   IP(dst="10.10.10.10", src="11.11.11.11") /
2088                   UDP(sport=1234, dport=1234) /
2089                   Raw('\xa5' * 100))
2090
2091         #
2092         # The BD is learning, so send in one of each packet to learn
2093         #
2094         p_core_encap = (Ether(dst=self.pg0.local_mac,
2095                               src=self.pg0.remote_mac) /
2096                         MPLS(label=55, ttl=64) /
2097                         p_core)
2098
2099         self.pg1.add_stream(p_cust)
2100         self.pg_enable_capture(self.pg_interfaces)
2101         self.pg_start()
2102         self.pg0.add_stream(p_core_encap)
2103         self.pg_enable_capture(self.pg_interfaces)
2104         self.pg_start()
2105
2106         # we've learnt this so expect it be be forwarded
2107         rx0 = self.pg1.get_capture(1)
2108
2109         self.assertEqual(rx0[0][Ether].dst, p_core[Ether].dst)
2110         self.assertEqual(rx0[0][Ether].src, p_core[Ether].src)
2111
2112         #
2113         # now a stream in each direction
2114         #
2115         self.pg1.add_stream(p_cust * NUM_PKTS)
2116         self.pg_enable_capture(self.pg_interfaces)
2117         self.pg_start()
2118
2119         rx0 = self.pg0.get_capture(NUM_PKTS)
2120
2121         self.verify_capture_tunneled_ethernet(rx0, p_cust*NUM_PKTS,
2122                                               [VppMplsLabel(42)])
2123
2124         #
2125         # remove interfaces from customers bridge-domain
2126         #
2127         self.vapi.sw_interface_set_l2_bridge(
2128             rx_sw_if_index=mpls_tun.sw_if_index, bd_id=1, enable=0)
2129         self.vapi.sw_interface_set_l2_bridge(
2130             rx_sw_if_index=self.pg1.sw_if_index, bd_id=1, enable=0)
2131
2132
2133 if __name__ == '__main__':
2134     unittest.main(testRunner=VppTestRunner)