make test: improve robustness and performance
[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 IpRoute, RoutePath, MplsRoute, MplsIpBind
8
9 from scapy.packet import Raw
10 from scapy.layers.l2 import Ether
11 from scapy.layers.inet import IP, UDP, ICMP
12 from scapy.layers.inet6 import IPv6
13 from scapy.contrib.mpls import MPLS
14
15 class TestMPLS(VppTestCase):
16     """ MPLS Test Case """
17
18     @classmethod
19     def setUpClass(cls):
20         super(TestMPLS, cls).setUpClass()
21
22     def setUp(self):
23         super(TestMPLS, self).setUp()
24
25         # create 2 pg interfaces
26         self.create_pg_interfaces(range(2))
27
28         # setup both interfaces
29         # assign them different tables.
30         table_id = 0
31
32         for i in self.pg_interfaces:
33             i.admin_up()
34             i.set_table_ip4(table_id)
35             i.set_table_ip6(table_id)
36             i.config_ip4()
37             i.resolve_arp()
38             i.config_ip6()
39             i.resolve_ndp()
40             i.enable_mpls()
41             table_id += 1
42
43     def tearDown(self):
44         super(TestMPLS, self).tearDown()
45
46     # the default of 64 matches the IP packet TTL default
47     def create_stream_labelled_ip4(self, src_if, mpls_labels, mpls_ttl=255, ping=0, ip_itf=None):
48         pkts = []
49         for i in range(0, 257):
50             info = self.create_packet_info(src_if.sw_if_index,
51                                            src_if.sw_if_index)
52             payload = self.info_to_payload(info)
53             p = Ether(dst=src_if.local_mac, src=src_if.remote_mac)
54
55             for ii in range(len(mpls_labels)):
56                 if ii == len(mpls_labels) - 1:
57                     p = p / MPLS(label=mpls_labels[ii], ttl=mpls_ttl, s=1)
58                 else:
59                     p = p / MPLS(label=mpls_labels[ii], ttl=mpls_ttl, s=0)
60             if not ping:
61                 p = (p / IP(src=src_if.local_ip4, dst=src_if.remote_ip4) /
62                      UDP(sport=1234, dport=1234) /
63                      Raw(payload))
64             else:
65                 p = (p / IP(src=ip_itf.remote_ip4,
66                             dst=ip_itf.local_ip4) /
67                      ICMP())
68
69             info.data = p.copy()
70             pkts.append(p)
71         return pkts
72
73     def create_stream_ip4(self, src_if, dst_ip):
74         pkts = []
75         for i in range(0, 257):
76             info = self.create_packet_info(src_if.sw_if_index,
77                                            src_if.sw_if_index)
78             payload = self.info_to_payload(info)
79             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
80                  IP(src=src_if.remote_ip4, dst=dst_ip) /
81                  UDP(sport=1234, dport=1234) /
82                  Raw(payload))
83             info.data = p.copy()
84             pkts.append(p)
85         return pkts
86
87     def create_stream_labelled_ip6(self, src_if, mpls_label, mpls_ttl):
88         pkts = []
89         for i in range(0, 257):
90             info = self.create_packet_info(src_if.sw_if_index,
91                                            src_if.sw_if_index)
92             payload = self.info_to_payload(info)
93             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
94                  MPLS(label=mpls_label, ttl=mpls_ttl) /
95                  IPv6(src=src_if.remote_ip6, dst=src_if.remote_ip6) /
96                  UDP(sport=1234, dport=1234) /
97                  Raw(payload))
98             info.data = p.copy()
99             pkts.append(p)
100         return pkts
101
102     @staticmethod
103     def verify_filter(capture, sent):
104         if not len(capture) == len(sent):
105             # filter out any IPv6 RAs from the capture
106             for p in capture:
107                 if p.haslayer(IPv6):
108                     capture.remove(p)
109         return capture
110
111     def verify_capture_ip4(self, src_if, capture, sent, ping_resp=0):
112         try:
113             capture = self.verify_filter(capture, sent)
114
115             self.assertEqual(len(capture), len(sent))
116
117             for i in range(len(capture)):
118                 tx = sent[i]
119                 rx = capture[i]
120
121                 # the rx'd packet has the MPLS label popped
122                 eth = rx[Ether]
123                 self.assertEqual(eth.type, 0x800)
124
125                 tx_ip = tx[IP]
126                 rx_ip = rx[IP]
127
128                 if not ping_resp:
129                     self.assertEqual(rx_ip.src, tx_ip.src)
130                     self.assertEqual(rx_ip.dst, tx_ip.dst)
131                     # IP processing post pop has decremented the TTL
132                     self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
133                 else:
134                     self.assertEqual(rx_ip.src, tx_ip.dst)
135                     self.assertEqual(rx_ip.dst, tx_ip.src)
136
137         except:
138             raise
139
140     def verify_mpls_stack(self, rx, mpls_labels, ttl=255, num=0):
141         # the rx'd packet has the MPLS label popped
142         eth = rx[Ether]
143         self.assertEqual(eth.type, 0x8847)
144
145         rx_mpls = rx[MPLS]
146
147         for ii in range(len(mpls_labels)):
148             self.assertEqual(rx_mpls.label, mpls_labels[ii])
149             self.assertEqual(rx_mpls.cos, 0)
150             if ii == num:
151                 self.assertEqual(rx_mpls.ttl, ttl)
152             else:
153                 self.assertEqual(rx_mpls.ttl, 255)
154
155             if ii == len(mpls_labels) - 1:
156                 self.assertEqual(rx_mpls.s, 1)
157             else:
158                 # not end of stack
159                 self.assertEqual(rx_mpls.s, 0)
160                 # pop the label to expose the next
161                 rx_mpls = rx_mpls[MPLS].payload
162
163     def verify_capture_labelled_ip4(self, src_if, capture, sent,
164                                     mpls_labels):
165         try:
166             capture = self.verify_filter(capture, sent)
167
168             self.assertEqual(len(capture), len(sent))
169
170             for i in range(len(capture)):
171                 tx = sent[i]
172                 rx = capture[i]
173                 tx_ip = tx[IP]
174                 rx_ip = rx[IP]
175
176                 # the MPLS TTL is copied from the IP
177                 self.verify_mpls_stack(
178                     rx, mpls_labels, rx_ip.ttl, len(mpls_labels) - 1)
179
180                 self.assertEqual(rx_ip.src, tx_ip.src)
181                 self.assertEqual(rx_ip.dst, tx_ip.dst)
182                 # IP processing post pop has decremented the TTL
183                 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
184
185         except:
186             raise
187
188     def verify_capture_tunneled_ip4(self, src_if, capture, sent, mpls_labels):
189         try:
190             capture = self.verify_filter(capture, sent)
191
192             self.assertEqual(len(capture), len(sent))
193
194             for i in range(len(capture)):
195                 tx = sent[i]
196                 rx = capture[i]
197                 tx_ip = tx[IP]
198                 rx_ip = rx[IP]
199
200                 # the MPLS TTL is 255 since it enters a new tunnel
201                 self.verify_mpls_stack(
202                     rx, mpls_labels, 255, len(mpls_labels) - 1)
203
204                 self.assertEqual(rx_ip.src, tx_ip.src)
205                 self.assertEqual(rx_ip.dst, tx_ip.dst)
206                 # IP processing post pop has decremented the TTL
207                 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
208
209         except:
210             raise
211
212     def verify_capture_labelled(self, src_if, capture, sent,
213                                 mpls_labels, ttl=254, num=0):
214         try:
215             capture = self.verify_filter(capture, sent)
216
217             self.assertEqual(len(capture), len(sent))
218
219             for i in range(len(capture)):
220                 rx = capture[i]
221                 self.verify_mpls_stack(rx, mpls_labels, ttl, num)
222         except:
223             raise
224
225     def verify_capture_ip6(self, src_if, capture, sent):
226         try:
227             self.assertEqual(len(capture), len(sent))
228
229             for i in range(len(capture)):
230                 tx = sent[i]
231                 rx = capture[i]
232
233                 # the rx'd packet has the MPLS label popped
234                 eth = rx[Ether]
235                 self.assertEqual(eth.type, 0x86DD)
236
237                 tx_ip = tx[IPv6]
238                 rx_ip = rx[IPv6]
239
240                 self.assertEqual(rx_ip.src, tx_ip.src)
241                 self.assertEqual(rx_ip.dst, tx_ip.dst)
242                 # IP processing post pop has decremented the TTL
243                 self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)
244
245         except:
246             raise
247
248     def test_swap(self):
249         """ MPLS label swap tests """
250
251         #
252         # A simple MPLS xconnect - eos label in label out
253         #
254         route_32_eos = MplsRoute(self, 32, 1,
255                                  [RoutePath(self.pg0.remote_ip4,
256                                             self.pg0.sw_if_index,
257                                             labels=[33])])
258         route_32_eos.add_vpp_config()
259
260         #
261         # a stream that matches the route for 10.0.0.1
262         # PG0 is in the default table
263         #
264         self.vapi.cli("clear trace")
265         tx = self.create_stream_labelled_ip4(self.pg0, [32])
266         self.pg0.add_stream(tx)
267
268         self.pg_enable_capture(self.pg_interfaces)
269         self.pg_start()
270
271         rx = self.pg0.get_capture()
272         self.verify_capture_labelled_ip4(self.pg0, rx, tx, [33])
273
274         #
275         # A simple MPLS xconnect - non-eos label in label out
276         #
277         route_32_neos = MplsRoute(self, 32, 0,
278                                   [RoutePath(self.pg0.remote_ip4,
279                                              self.pg0.sw_if_index,
280                                              labels=[33])])
281         route_32_neos.add_vpp_config()
282
283         #
284         # a stream that matches the route for 10.0.0.1
285         # PG0 is in the default table
286         #
287         self.vapi.cli("clear trace")
288         tx = self.create_stream_labelled_ip4(self.pg0, [32, 99])
289         self.pg0.add_stream(tx)
290
291         self.pg_enable_capture(self.pg_interfaces)
292         self.pg_start()
293
294         rx = self.pg0.get_capture()
295         self.verify_capture_labelled(self.pg0, rx, tx, [33, 99])
296
297         #
298         # An MPLS xconnect - EOS label in IP out
299         #
300         route_33_eos = MplsRoute(self, 33, 1,
301                                  [RoutePath(self.pg0.remote_ip4,
302                                             self.pg0.sw_if_index,
303                                             labels=[])])
304         route_33_eos.add_vpp_config()
305
306         self.vapi.cli("clear trace")
307         tx = self.create_stream_labelled_ip4(self.pg0, [33])
308         self.pg0.add_stream(tx)
309
310         self.pg_enable_capture(self.pg_interfaces)
311         self.pg_start()
312
313         rx = self.pg0.get_capture()
314         self.verify_capture_ip4(self.pg0, rx, tx)
315
316         #
317         # An MPLS xconnect - non-EOS label in IP out - an invalid configuration
318         # so this traffic should be dropped.
319         #
320         route_33_neos = MplsRoute(self, 33, 0,
321                                   [RoutePath(self.pg0.remote_ip4,
322                                              self.pg0.sw_if_index,
323                                              labels=[])])
324         route_33_neos.add_vpp_config()
325
326         self.vapi.cli("clear trace")
327         tx = self.create_stream_labelled_ip4(self.pg0, [33, 99])
328         self.pg0.add_stream(tx)
329
330         self.pg_enable_capture(self.pg_interfaces)
331         self.pg_start()
332         self.pg0.assert_nothing_captured(
333             remark="MPLS non-EOS packets popped and forwarded")
334
335         #
336         # A recursive EOS x-connect, which resolves through another x-connect
337         #
338         route_34_eos = MplsRoute(self, 34, 1,
339                                  [RoutePath("0.0.0.0",
340                                             0xffffffff,
341                                             nh_via_label=32,
342                                             labels=[44, 45])])
343         route_34_eos.add_vpp_config()
344
345         self.vapi.cli("clear trace")
346         tx = self.create_stream_labelled_ip4(self.pg0, [34])
347         self.pg0.add_stream(tx)
348
349         self.pg_enable_capture(self.pg_interfaces)
350         self.pg_start()
351
352         rx = self.pg0.get_capture()
353         self.verify_capture_labelled_ip4(self.pg0, rx, tx, [33, 44, 45])
354
355         #
356         # A recursive non-EOS x-connect, which resolves through another
357         # x-connect
358         #
359         route_34_neos = MplsRoute(self, 34, 0,
360                                   [RoutePath("0.0.0.0",
361                                              0xffffffff,
362                                              nh_via_label=32,
363                                              labels=[44, 46])])
364         route_34_neos.add_vpp_config()
365
366         self.vapi.cli("clear trace")
367         tx = self.create_stream_labelled_ip4(self.pg0, [34, 99])
368         self.pg0.add_stream(tx)
369
370         self.pg_enable_capture(self.pg_interfaces)
371         self.pg_start()
372
373         rx = self.pg0.get_capture()
374         # it's the 2nd (counting from 0) label in the stack that is swapped
375         self.verify_capture_labelled(self.pg0, rx, tx, [33, 44, 46, 99], num=2)
376
377         #
378         # an recursive IP route that resolves through the recursive non-eos
379         # x-connect
380         #
381         ip_10_0_0_1 = IpRoute(self, "10.0.0.1", 32,
382                               [RoutePath("0.0.0.0",
383                                          0xffffffff,
384                                          nh_via_label=34,
385                                          labels=[55])])
386         ip_10_0_0_1.add_vpp_config()
387
388         self.vapi.cli("clear trace")
389         tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
390         self.pg0.add_stream(tx)
391
392         self.pg_enable_capture(self.pg_interfaces)
393         self.pg_start()
394
395         rx = self.pg0.get_capture()
396         self.verify_capture_labelled_ip4(self.pg0, rx, tx, [33, 44, 46, 55])
397
398         ip_10_0_0_1.remove_vpp_config()
399         route_34_neos.remove_vpp_config()
400         route_34_eos.remove_vpp_config()
401         route_33_neos.remove_vpp_config()
402         route_33_eos.remove_vpp_config()
403         route_32_neos.remove_vpp_config()
404         route_32_eos.remove_vpp_config()
405
406     def test_bind(self):
407         """ MPLS Local Label Binding test """
408
409         #
410         # Add a non-recursive route with a single out label
411         #
412         route_10_0_0_1 = IpRoute(self, "10.0.0.1", 32,
413                                  [RoutePath(self.pg0.remote_ip4,
414                                             self.pg0.sw_if_index,
415                                             labels=[45])])
416         route_10_0_0_1.add_vpp_config()
417
418         # bind a local label to the route
419         binding = MplsIpBind(self, 44, "10.0.0.1", 32)
420         binding.add_vpp_config()
421
422         # non-EOS stream
423         self.vapi.cli("clear trace")
424         tx = self.create_stream_labelled_ip4(self.pg0, [44, 99])
425         self.pg0.add_stream(tx)
426
427         self.pg_enable_capture(self.pg_interfaces)
428         self.pg_start()
429
430         rx = self.pg0.get_capture()
431         self.verify_capture_labelled(self.pg0, rx, tx, [45, 99])
432
433         # EOS stream
434         self.vapi.cli("clear trace")
435         tx = self.create_stream_labelled_ip4(self.pg0, [44])
436         self.pg0.add_stream(tx)
437
438         self.pg_enable_capture(self.pg_interfaces)
439         self.pg_start()
440
441         rx = self.pg0.get_capture()
442         self.verify_capture_labelled(self.pg0, rx, tx, [45])
443
444         # IP stream
445         self.vapi.cli("clear trace")
446         tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
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_labelled_ip4(self.pg0, rx, tx, [45])
454
455         #
456         # cleanup
457         #
458         binding.remove_vpp_config()
459         route_10_0_0_1.remove_vpp_config()
460
461     def test_imposition(self):
462         """ MPLS label imposition test """
463
464         #
465         # Add a non-recursive route with a single out label
466         #
467         route_10_0_0_1 = IpRoute(self, "10.0.0.1", 32,
468                                  [RoutePath(self.pg0.remote_ip4,
469                                             self.pg0.sw_if_index,
470                                             labels=[32])])
471         route_10_0_0_1.add_vpp_config()
472
473         #
474         # a stream that matches the route for 10.0.0.1
475         # PG0 is in the default table
476         #
477         self.vapi.cli("clear trace")
478         tx = self.create_stream_ip4(self.pg0, "10.0.0.1")
479         self.pg0.add_stream(tx)
480
481         self.pg_enable_capture(self.pg_interfaces)
482         self.pg_start()
483
484         rx = self.pg0.get_capture()
485         self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32])
486
487         #
488         # Add a non-recursive route with a 3 out labels
489         #
490         route_10_0_0_2 = IpRoute(self, "10.0.0.2", 32,
491                                  [RoutePath(self.pg0.remote_ip4,
492                                             self.pg0.sw_if_index,
493                                             labels=[32, 33, 34])])
494         route_10_0_0_2.add_vpp_config()
495
496         #
497         # a stream that matches the route for 10.0.0.1
498         # PG0 is in the default table
499         #
500         self.vapi.cli("clear trace")
501         tx = self.create_stream_ip4(self.pg0, "10.0.0.2")
502         self.pg0.add_stream(tx)
503
504         self.pg_enable_capture(self.pg_interfaces)
505         self.pg_start()
506
507         rx = self.pg0.get_capture()
508         self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32, 33, 34])
509
510         #
511         # add a recursive path, with output label, via the 1 label route
512         #
513         route_11_0_0_1 = IpRoute(self, "11.0.0.1", 32,
514                                  [RoutePath("10.0.0.1",
515                                             0xffffffff,
516                                             labels=[44])])
517         route_11_0_0_1.add_vpp_config()
518
519         #
520         # a stream that matches the route for 11.0.0.1, should pick up
521         # the label stack for 11.0.0.1 and 10.0.0.1
522         #
523         self.vapi.cli("clear trace")
524         tx = self.create_stream_ip4(self.pg0, "11.0.0.1")
525         self.pg0.add_stream(tx)
526
527         self.pg_enable_capture(self.pg_interfaces)
528         self.pg_start()
529
530         rx = self.pg0.get_capture()
531         self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32, 44])
532
533         #
534         # add a recursive path, with 2 labels, via the 3 label route
535         #
536         route_11_0_0_2 = IpRoute(self, "11.0.0.2", 32,
537                                  [RoutePath("10.0.0.2",
538                                             0xffffffff,
539                                             labels=[44, 45])])
540         route_11_0_0_2.add_vpp_config()
541
542         #
543         # a stream that matches the route for 11.0.0.1, should pick up
544         # the label stack for 11.0.0.1 and 10.0.0.1
545         #
546         self.vapi.cli("clear trace")
547         tx = self.create_stream_ip4(self.pg0, "11.0.0.2")
548         self.pg0.add_stream(tx)
549
550         self.pg_enable_capture(self.pg_interfaces)
551         self.pg_start()
552
553         rx = self.pg0.get_capture()
554         self.verify_capture_labelled_ip4(
555             self.pg0, rx, tx, [32, 33, 34, 44, 45])
556
557         #
558         # cleanup
559         #
560         route_11_0_0_2.remove_vpp_config()
561         route_11_0_0_1.remove_vpp_config()
562         route_10_0_0_2.remove_vpp_config()
563         route_10_0_0_1.remove_vpp_config()
564
565     def test_tunnel(self):
566         """ MPLS Tunnel Tests """
567
568         #
569         # Create a tunnel with a single out label
570         #
571         nh_addr = socket.inet_pton(socket.AF_INET, self.pg0.remote_ip4)
572
573         reply = self.vapi.mpls_tunnel_add_del(
574             0xffffffff,  # don't know the if index yet
575             1,  # IPv4 next-hop
576             nh_addr,
577             self.pg0.sw_if_index,
578             0,  # next-hop-table-id
579             1,  # next-hop-weight
580             2,  # num-out-labels,
581             [44, 46])
582         self.vapi.sw_interface_set_flags(reply.sw_if_index, admin_up_down=1)
583
584         #
585         # add an unlabelled route through the new tunnel
586         #
587         dest_addr = socket.inet_pton(socket.AF_INET, "10.0.0.3")
588         nh_addr = socket.inet_pton(socket.AF_INET, "0.0.0.0")
589         dest_addr_len = 32
590
591         self.vapi.ip_add_del_route(
592             dest_addr,
593             dest_addr_len,
594             nh_addr,  # all zeros next-hop - tunnel is p2p
595             reply.sw_if_index,  # sw_if_index of the new tunnel
596             0,  # table-id
597             0,  # next-hop-table-id
598             1,  # next-hop-weight
599             0,  # num-out-labels,
600             [])  # out-label
601
602         self.vapi.cli("clear trace")
603         tx = self.create_stream_ip4(self.pg0, "10.0.0.3")
604         self.pg0.add_stream(tx)
605
606         self.pg_enable_capture(self.pg_interfaces)
607         self.pg_start()
608
609         rx = self.pg0.get_capture()
610         self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [44, 46])
611
612     def test_v4_exp_null(self):
613         """ MPLS V4 Explicit NULL test """
614
615         #
616         # The first test case has an MPLS TTL of 0
617         # all packet should be dropped
618         #
619         tx = self.create_stream_labelled_ip4(self.pg0, [0], 0)
620         self.pg0.add_stream(tx)
621
622         self.pg_enable_capture(self.pg_interfaces)
623         self.pg_start()
624
625         self.pg0.assert_nothing_captured(remark="MPLS TTL=0 packets forwarded")
626
627         #
628         # a stream with a non-zero MPLS TTL
629         # PG0 is in the default table
630         #
631         self.vapi.cli("clear trace")
632         tx = self.create_stream_labelled_ip4(self.pg0, [0])
633         self.pg0.add_stream(tx)
634
635         self.pg_enable_capture(self.pg_interfaces)
636         self.pg_start()
637
638         rx = self.pg0.get_capture()
639         self.verify_capture_ip4(self.pg0, rx, tx)
640
641         #
642         # a stream with a non-zero MPLS TTL
643         # PG1 is in table 1
644         # we are ensuring the post-pop lookup occurs in the VRF table
645         #
646         self.vapi.cli("clear trace")
647         tx = self.create_stream_labelled_ip4(self.pg1, [0])
648         self.pg1.add_stream(tx)
649
650         self.pg_enable_capture(self.pg_interfaces)
651         self.pg_start()
652
653         rx = self.pg1.get_capture()
654         self.verify_capture_ip4(self.pg0, rx, tx)
655
656     def test_v6_exp_null(self):
657         """ MPLS V6 Explicit NULL test """
658
659         #
660         # a stream with a non-zero MPLS TTL
661         # PG0 is in the default table
662         #
663         self.vapi.cli("clear trace")
664         tx = self.create_stream_labelled_ip6(self.pg0, 2, 2)
665         self.pg0.add_stream(tx)
666
667         self.pg_enable_capture(self.pg_interfaces)
668         self.pg_start()
669
670         rx = self.pg0.get_capture()
671         self.verify_capture_ip6(self.pg0, rx, tx)
672
673         #
674         # a stream with a non-zero MPLS TTL
675         # PG1 is in table 1
676         # we are ensuring the post-pop lookup occurs in the VRF table
677         #
678         self.vapi.cli("clear trace")
679         tx = self.create_stream_labelled_ip6(self.pg1, 2, 2)
680         self.pg1.add_stream(tx)
681
682         self.pg_enable_capture(self.pg_interfaces)
683         self.pg_start()
684
685         rx = self.pg1.get_capture()
686         self.verify_capture_ip6(self.pg0, rx, tx)
687
688     def test_deag(self):
689         """ MPLS Deagg """
690
691         #
692         # A de-agg route - next-hop lookup in default table
693         #
694         route_34_eos = MplsRoute(self, 34, 1,
695                                   [RoutePath("0.0.0.0",
696                                              0xffffffff,
697                                              nh_table_id=0)])
698         route_34_eos.add_vpp_config()
699
700         #
701         # ping an interface in the default table
702         # PG0 is in the default table
703         #
704         self.vapi.cli("clear trace")
705         tx = self.create_stream_labelled_ip4(self.pg0, [34], ping=1,
706                                              ip_itf=self.pg0)
707         self.pg0.add_stream(tx)
708
709         self.pg_enable_capture(self.pg_interfaces)
710         self.pg_start()
711
712         rx = self.pg0.get_capture()
713         self.verify_capture_ip4(self.pg0, rx, tx, ping_resp=1)
714
715         #
716         # A de-agg route - next-hop lookup in non-default table
717         #
718         route_35_eos = MplsRoute(self, 35, 1,
719                                   [RoutePath("0.0.0.0",
720                                              0xffffffff,
721                                              nh_table_id=1)])
722         route_35_eos.add_vpp_config()
723
724         #
725         # ping an interface in the non-default table
726         # PG0 is in the default table. packet arrive labelled in the
727         # default table and egress unlabelled in the non-default
728         #
729         self.vapi.cli("clear trace")
730         tx = self.create_stream_labelled_ip4(self.pg0, [35], ping=1, ip_itf=self.pg1)
731         self.pg0.add_stream(tx)
732
733         self.pg_enable_capture(self.pg_interfaces)
734         self.pg_start()
735
736         rx = self.pg1.get_capture()
737         self.verify_capture_ip4(self.pg1, rx, tx, ping_resp=1)
738
739         route_35_eos.remove_vpp_config()
740         route_34_eos.remove_vpp_config()
741
742 if __name__ == '__main__':
743     unittest.main(testRunner=VppTestRunner)