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