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