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