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