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