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