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