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