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