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