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