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