ip: support flow-hash gtpv1teid
[vpp.git] / test / test_ip4.py
1 #!/usr/bin/env python3
2 import binascii
3 import random
4 import socket
5 import unittest
6
7 import scapy.compat
8 from scapy.contrib.mpls import MPLS
9 from scapy.contrib.gtp import GTP_U_Header
10 from scapy.layers.inet import IP, UDP, TCP, ICMP, icmptypes, icmpcodes
11 from scapy.layers.inet6 import IPv6
12 from scapy.layers.l2 import Ether, Dot1Q, ARP
13 from scapy.packet import Raw
14 from six import moves
15
16 from framework import tag_fixme_vpp_workers
17 from framework import VppTestCase, VppTestRunner
18 from util import ppp
19 from vpp_ip_route import (
20     VppIpRoute,
21     VppRoutePath,
22     VppIpMRoute,
23     VppMRoutePath,
24     VppMplsIpBind,
25     VppMplsTable,
26     VppIpTable,
27     FibPathType,
28     find_route,
29     VppIpInterfaceAddress,
30     find_route_in_dump,
31     find_mroute_in_dump,
32 )
33 from vpp_ip import VppIpPuntPolicer, VppIpPuntRedirect, VppIpPathMtu
34 from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint
35 from vpp_papi import vpp_papi, VppEnum
36 from vpp_neighbor import VppNeighbor
37 from vpp_lo_interface import VppLoInterface
38 from vpp_policer import VppPolicer, PolicerAction
39
40 NUM_PKTS = 67
41
42
43 class TestIPv4(VppTestCase):
44     """IPv4 Test Case"""
45
46     @classmethod
47     def setUpClass(cls):
48         super(TestIPv4, cls).setUpClass()
49
50     @classmethod
51     def tearDownClass(cls):
52         super(TestIPv4, cls).tearDownClass()
53
54     def setUp(self):
55         """
56         Perform test setup before test case.
57
58         **Config:**
59             - create 3 pg interfaces
60                 - untagged pg0 interface
61                 - Dot1Q subinterface on pg1
62                 - Dot1AD subinterface on pg2
63             - setup interfaces:
64                 - put it into UP state
65                 - set IPv4 addresses
66                 - resolve neighbor address using ARP
67             - configure 200 fib entries
68
69         :ivar list interfaces: pg interfaces and subinterfaces.
70         :ivar dict flows: IPv4 packet flows in test.
71         """
72         super(TestIPv4, self).setUp()
73
74         # create 3 pg interfaces
75         self.create_pg_interfaces(range(3))
76
77         # create 2 subinterfaces for pg1 and pg2
78         self.sub_interfaces = [
79             VppDot1QSubint(self, self.pg1, 100),
80             VppDot1ADSubint(self, self.pg2, 200, 300, 400),
81         ]
82
83         # packet flows mapping pg0 -> pg1.sub, pg2.sub, etc.
84         self.flows = dict()
85         self.flows[self.pg0] = [self.pg1.sub_if, self.pg2.sub_if]
86         self.flows[self.pg1.sub_if] = [self.pg0, self.pg2.sub_if]
87         self.flows[self.pg2.sub_if] = [self.pg0, self.pg1.sub_if]
88
89         # packet sizes
90         self.pg_if_packet_sizes = [64, 1500, 9020]
91
92         self.interfaces = list(self.pg_interfaces)
93         self.interfaces.extend(self.sub_interfaces)
94
95         # setup all interfaces
96         for i in self.interfaces:
97             i.admin_up()
98             i.config_ip4()
99             i.resolve_arp()
100
101         # config 2M FIB entries
102
103     def tearDown(self):
104         """Run standard test teardown and log ``show ip arp``."""
105         super(TestIPv4, self).tearDown()
106
107     def show_commands_at_teardown(self):
108         self.logger.info(self.vapi.cli("show ip4 neighbors"))
109         # info(self.vapi.cli("show ip fib"))  # many entries
110
111     def modify_packet(self, src_if, packet_size, pkt):
112         """Add load, set destination IP and extend packet to required packet
113         size for defined interface.
114
115         :param VppInterface src_if: Interface to create packet for.
116         :param int packet_size: Required packet size.
117         :param Scapy pkt: Packet to be modified.
118         """
119         dst_if_idx = int(packet_size / 10 % 2)
120         dst_if = self.flows[src_if][dst_if_idx]
121         info = self.create_packet_info(src_if, dst_if)
122         payload = self.info_to_payload(info)
123         p = pkt / Raw(payload)
124         p[IP].dst = dst_if.remote_ip4
125         info.data = p.copy()
126         if isinstance(src_if, VppSubInterface):
127             p = src_if.add_dot1_layer(p)
128         self.extend_packet(p, packet_size)
129
130         return p
131
132     def create_stream(self, src_if):
133         """Create input packet stream for defined interface.
134
135         :param VppInterface src_if: Interface to create packet stream for.
136         """
137         hdr_ext = 4 if isinstance(src_if, VppSubInterface) else 0
138         pkt_tmpl = (
139             Ether(dst=src_if.local_mac, src=src_if.remote_mac)
140             / IP(src=src_if.remote_ip4)
141             / UDP(sport=1234, dport=1234)
142         )
143
144         pkts = [
145             self.modify_packet(src_if, i, pkt_tmpl)
146             for i in moves.range(
147                 self.pg_if_packet_sizes[0], self.pg_if_packet_sizes[1], 10
148             )
149         ]
150         pkts_b = [
151             self.modify_packet(src_if, i, pkt_tmpl)
152             for i in moves.range(
153                 self.pg_if_packet_sizes[1] + hdr_ext,
154                 self.pg_if_packet_sizes[2] + hdr_ext,
155                 50,
156             )
157         ]
158         pkts.extend(pkts_b)
159
160         return pkts
161
162     def verify_capture(self, dst_if, capture):
163         """Verify captured input packet stream for defined interface.
164
165         :param VppInterface dst_if: Interface to verify captured packet stream
166             for.
167         :param list capture: Captured packet stream.
168         """
169         self.logger.info("Verifying capture on interface %s" % dst_if.name)
170         last_info = dict()
171         for i in self.interfaces:
172             last_info[i.sw_if_index] = None
173         is_sub_if = False
174         dst_sw_if_index = dst_if.sw_if_index
175         if hasattr(dst_if, "parent"):
176             is_sub_if = True
177         for packet in capture:
178             if is_sub_if:
179                 # Check VLAN tags and Ethernet header
180                 packet = dst_if.remove_dot1_layer(packet)
181             self.assertTrue(Dot1Q not in packet)
182             try:
183                 ip = packet[IP]
184                 udp = packet[UDP]
185                 payload_info = self.payload_to_info(packet[Raw])
186                 packet_index = payload_info.index
187                 self.assertEqual(payload_info.dst, dst_sw_if_index)
188                 self.logger.debug(
189                     "Got packet on port %s: src=%u (id=%u)"
190                     % (dst_if.name, payload_info.src, packet_index)
191                 )
192                 next_info = self.get_next_packet_info_for_interface2(
193                     payload_info.src, dst_sw_if_index, last_info[payload_info.src]
194                 )
195                 last_info[payload_info.src] = next_info
196                 self.assertTrue(next_info is not None)
197                 self.assertEqual(packet_index, next_info.index)
198                 saved_packet = next_info.data
199                 # Check standard fields
200                 self.assertEqual(ip.src, saved_packet[IP].src)
201                 self.assertEqual(ip.dst, saved_packet[IP].dst)
202                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
203                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
204             except:
205                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
206                 raise
207         for i in self.interfaces:
208             remaining_packet = self.get_next_packet_info_for_interface2(
209                 i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index]
210             )
211             self.assertTrue(
212                 remaining_packet is None,
213                 "Interface %s: Packet expected from interface %s "
214                 "didn't arrive" % (dst_if.name, i.name),
215             )
216
217     def test_fib(self):
218         """IPv4 FIB test
219
220         Test scenario:
221
222             - Create IPv4 stream for pg0 interface
223             - Create IPv4 tagged streams for pg1's and pg2's sub-interface.
224             - Send and verify received packets on each interface.
225         """
226
227         pkts = self.create_stream(self.pg0)
228         self.pg0.add_stream(pkts)
229
230         for i in self.sub_interfaces:
231             pkts = self.create_stream(i)
232             i.parent.add_stream(pkts)
233
234         self.pg_enable_capture(self.pg_interfaces)
235         self.pg_start()
236
237         pkts = self.pg0.get_capture()
238         self.verify_capture(self.pg0, pkts)
239
240         for i in self.sub_interfaces:
241             pkts = i.parent.get_capture()
242             self.verify_capture(i, pkts)
243
244
245 class TestIPv4RouteLookup(VppTestCase):
246     """IPv4 Route Lookup Test Case"""
247
248     routes = []
249
250     def route_lookup(self, prefix, exact):
251         return self.vapi.api(
252             self.vapi.papi.ip_route_lookup,
253             {
254                 "table_id": 0,
255                 "exact": exact,
256                 "prefix": prefix,
257             },
258         )
259
260     @classmethod
261     def setUpClass(cls):
262         super(TestIPv4RouteLookup, cls).setUpClass()
263
264     @classmethod
265     def tearDownClass(cls):
266         super(TestIPv4RouteLookup, cls).tearDownClass()
267
268     def setUp(self):
269         super(TestIPv4RouteLookup, self).setUp()
270
271         drop_nh = VppRoutePath(
272             "127.0.0.1", 0xFFFFFFFF, type=FibPathType.FIB_PATH_TYPE_DROP
273         )
274
275         # Add 3 routes
276         r = VppIpRoute(self, "1.1.0.0", 16, [drop_nh])
277         r.add_vpp_config()
278         self.routes.append(r)
279
280         r = VppIpRoute(self, "1.1.1.0", 24, [drop_nh])
281         r.add_vpp_config()
282         self.routes.append(r)
283
284         r = VppIpRoute(self, "1.1.1.1", 32, [drop_nh])
285         r.add_vpp_config()
286         self.routes.append(r)
287
288     def tearDown(self):
289         # Remove the routes we added
290         for r in self.routes:
291             r.remove_vpp_config()
292
293         super(TestIPv4RouteLookup, self).tearDown()
294
295     def test_exact_match(self):
296         # Verify we find the host route
297         prefix = "1.1.1.1/32"
298         result = self.route_lookup(prefix, True)
299         assert prefix == str(result.route.prefix)
300
301         # Verify we find a middle prefix route
302         prefix = "1.1.1.0/24"
303         result = self.route_lookup(prefix, True)
304         assert prefix == str(result.route.prefix)
305
306         # Verify we do not find an available LPM.
307         with self.vapi.assert_negative_api_retval():
308             self.route_lookup("1.1.1.2/32", True)
309
310     def test_longest_prefix_match(self):
311         # verify we find lpm
312         lpm_prefix = "1.1.1.0/24"
313         result = self.route_lookup("1.1.1.2/32", False)
314         assert lpm_prefix == str(result.route.prefix)
315
316         # Verify we find the exact when not requested
317         result = self.route_lookup(lpm_prefix, False)
318         assert lpm_prefix == str(result.route.prefix)
319
320         # Can't seem to delete the default route so no negative LPM test.
321
322
323 class TestIPv4IfAddrRoute(VppTestCase):
324     """IPv4 Interface Addr Route Test Case"""
325
326     @classmethod
327     def setUpClass(cls):
328         super(TestIPv4IfAddrRoute, cls).setUpClass()
329
330     @classmethod
331     def tearDownClass(cls):
332         super(TestIPv4IfAddrRoute, cls).tearDownClass()
333
334     def setUp(self):
335         super(TestIPv4IfAddrRoute, self).setUp()
336
337         # create 1 pg interface
338         self.create_pg_interfaces(range(1))
339
340         for i in self.pg_interfaces:
341             i.admin_up()
342             i.config_ip4()
343             i.resolve_arp()
344
345     def tearDown(self):
346         super(TestIPv4IfAddrRoute, self).tearDown()
347         for i in self.pg_interfaces:
348             i.unconfig_ip4()
349             i.admin_down()
350
351     def test_ipv4_ifaddrs_same_prefix(self):
352         """IPv4 Interface Addresses Same Prefix test
353
354         Test scenario:
355
356             - Verify no route in FIB for prefix 10.10.10.0/24
357             - Configure IPv4 address 10.10.10.10/24 on an interface
358             - Verify route in FIB for prefix 10.10.10.0/24
359             - Configure IPv4 address 10.10.10.20/24 on an interface
360             - Delete 10.10.10.10/24 from interface
361             - Verify route in FIB for prefix 10.10.10.0/24
362             - Delete 10.10.10.20/24 from interface
363             - Verify no route in FIB for prefix 10.10.10.0/24
364         """
365
366         # create two addresses, verify route not present
367         if_addr1 = VppIpInterfaceAddress(self, self.pg0, "10.10.10.10", 24)
368         if_addr2 = VppIpInterfaceAddress(self, self.pg0, "10.10.10.20", 24)
369         self.assertFalse(if_addr1.query_vpp_config())  # 10.10.10.10/24
370         self.assertFalse(find_route(self, "10.10.10.10", 32))
371         self.assertFalse(find_route(self, "10.10.10.20", 32))
372         self.assertFalse(find_route(self, "10.10.10.255", 32))
373         self.assertFalse(find_route(self, "10.10.10.0", 32))
374
375         # configure first address, verify route present
376         if_addr1.add_vpp_config()
377         self.assertTrue(if_addr1.query_vpp_config())  # 10.10.10.10/24
378         self.assertTrue(find_route(self, "10.10.10.10", 32))
379         self.assertFalse(find_route(self, "10.10.10.20", 32))
380         self.assertTrue(find_route(self, "10.10.10.255", 32))
381         self.assertTrue(find_route(self, "10.10.10.0", 32))
382
383         # configure second address, delete first, verify route not removed
384         if_addr2.add_vpp_config()
385         if_addr1.remove_vpp_config()
386         self.assertFalse(if_addr1.query_vpp_config())  # 10.10.10.10/24
387         self.assertTrue(if_addr2.query_vpp_config())  # 10.10.10.20/24
388         self.assertFalse(find_route(self, "10.10.10.10", 32))
389         self.assertTrue(find_route(self, "10.10.10.20", 32))
390         self.assertTrue(find_route(self, "10.10.10.255", 32))
391         self.assertTrue(find_route(self, "10.10.10.0", 32))
392
393         # delete second address, verify route removed
394         if_addr2.remove_vpp_config()
395         self.assertFalse(if_addr2.query_vpp_config())  # 10.10.10.20/24
396         self.assertFalse(find_route(self, "10.10.10.10", 32))
397         self.assertFalse(find_route(self, "10.10.10.20", 32))
398         self.assertFalse(find_route(self, "10.10.10.255", 32))
399         self.assertFalse(find_route(self, "10.10.10.0", 32))
400
401     def test_ipv4_ifaddr_route(self):
402         """IPv4 Interface Address Route test
403
404         Test scenario:
405
406             - Create loopback
407             - Configure IPv4 address on loopback
408             - Verify that address is not in the FIB
409             - Bring loopback up
410             - Verify that address is in the FIB now
411             - Bring loopback down
412             - Verify that address is not in the FIB anymore
413             - Bring loopback up
414             - Configure IPv4 address on loopback
415             - Verify that address is in the FIB now
416         """
417
418         # create a loopback and configure IPv4
419         loopbacks = self.create_loopback_interfaces(1)
420         lo_if = self.lo_interfaces[0]
421
422         lo_if.local_ip4_prefix_len = 32
423         lo_if.config_ip4()
424
425         # The intf was down when addr was added -> entry not in FIB
426         fib4_dump = self.vapi.ip_route_dump(0)
427         self.assertFalse(lo_if.is_ip4_entry_in_fib_dump(fib4_dump))
428
429         # When intf is brought up, entry is added
430         lo_if.admin_up()
431         fib4_dump = self.vapi.ip_route_dump(0)
432         self.assertTrue(lo_if.is_ip4_entry_in_fib_dump(fib4_dump))
433
434         # When intf is brought down, entry is removed
435         lo_if.admin_down()
436         fib4_dump = self.vapi.ip_route_dump(0)
437         self.assertFalse(lo_if.is_ip4_entry_in_fib_dump(fib4_dump))
438
439         # Remove addr, bring up interface, re-add -> entry in FIB
440         lo_if.unconfig_ip4()
441         lo_if.admin_up()
442         lo_if.config_ip4()
443         fib4_dump = self.vapi.ip_route_dump(0)
444         self.assertTrue(lo_if.is_ip4_entry_in_fib_dump(fib4_dump))
445
446     def test_ipv4_ifaddr_del(self):
447         """Delete an interface address that does not exist"""
448
449         loopbacks = self.create_loopback_interfaces(1)
450         lo = self.lo_interfaces[0]
451
452         lo.config_ip4()
453         lo.admin_up()
454
455         #
456         # try and remove pg0's subnet from lo
457         #
458         with self.vapi.assert_negative_api_retval():
459             self.vapi.sw_interface_add_del_address(
460                 sw_if_index=lo.sw_if_index, prefix=self.pg0.local_ip4_prefix, is_add=0
461             )
462
463
464 class TestICMPEcho(VppTestCase):
465     """ICMP Echo Test Case"""
466
467     @classmethod
468     def setUpClass(cls):
469         super(TestICMPEcho, cls).setUpClass()
470
471     @classmethod
472     def tearDownClass(cls):
473         super(TestICMPEcho, cls).tearDownClass()
474
475     def setUp(self):
476         super(TestICMPEcho, self).setUp()
477
478         # create 1 pg interface
479         self.create_pg_interfaces(range(1))
480
481         for i in self.pg_interfaces:
482             i.admin_up()
483             i.config_ip4()
484             i.resolve_arp()
485
486     def tearDown(self):
487         super(TestICMPEcho, self).tearDown()
488         for i in self.pg_interfaces:
489             i.unconfig_ip4()
490             i.admin_down()
491
492     def test_icmp_echo(self):
493         """VPP replies to ICMP Echo Request
494
495         Test scenario:
496
497             - Receive ICMP Echo Request message on pg0 interface.
498             - Check outgoing ICMP Echo Reply message on pg0 interface.
499         """
500
501         icmp_id = 0xB
502         icmp_seq = 5
503         icmp_load = b"\x0a" * 18
504         p_echo_request = (
505             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
506             / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
507             / ICMP(id=icmp_id, seq=icmp_seq)
508             / Raw(load=icmp_load)
509         )
510
511         self.pg0.add_stream(p_echo_request)
512         self.pg_enable_capture(self.pg_interfaces)
513         self.pg_start()
514
515         rx = self.pg0.get_capture(1)
516         rx = rx[0]
517         ether = rx[Ether]
518         ipv4 = rx[IP]
519         icmp = rx[ICMP]
520
521         self.assertEqual(ether.src, self.pg0.local_mac)
522         self.assertEqual(ether.dst, self.pg0.remote_mac)
523
524         self.assertEqual(ipv4.src, self.pg0.local_ip4)
525         self.assertEqual(ipv4.dst, self.pg0.remote_ip4)
526
527         self.assertEqual(icmptypes[icmp.type], "echo-reply")
528         self.assertEqual(icmp.id, icmp_id)
529         self.assertEqual(icmp.seq, icmp_seq)
530         self.assertEqual(icmp[Raw].load, icmp_load)
531
532
533 class TestIPv4FibCrud(VppTestCase):
534     """FIB - add/update/delete - ip4 routes
535
536     Test scenario:
537         - add 1k,
538         - del 100,
539         - add new 1k,
540         - del 1.5k
541
542     ..note:: Python API is too slow to add many routes, needs replacement.
543     """
544
545     def config_fib_many_to_one(self, start_dest_addr, next_hop_addr, count, start=0):
546         """
547
548         :param start_dest_addr:
549         :param next_hop_addr:
550         :param count:
551         :return list: added ips with 32 prefix
552         """
553         routes = []
554         for i in range(count):
555             r = VppIpRoute(
556                 self,
557                 start_dest_addr % (i + start),
558                 32,
559                 [VppRoutePath(next_hop_addr, 0xFFFFFFFF)],
560             )
561             r.add_vpp_config()
562             routes.append(r)
563         return routes
564
565     def unconfig_fib_many_to_one(self, start_dest_addr, next_hop_addr, count, start=0):
566
567         routes = []
568         for i in range(count):
569             r = VppIpRoute(
570                 self,
571                 start_dest_addr % (i + start),
572                 32,
573                 [VppRoutePath(next_hop_addr, 0xFFFFFFFF)],
574             )
575             r.remove_vpp_config()
576             routes.append(r)
577         return routes
578
579     def create_stream(self, src_if, dst_if, routes, count):
580         pkts = []
581
582         for _ in range(count):
583             dst_addr = random.choice(routes).prefix.network_address
584             info = self.create_packet_info(src_if, dst_if)
585             payload = self.info_to_payload(info)
586             p = (
587                 Ether(dst=src_if.local_mac, src=src_if.remote_mac)
588                 / IP(src=src_if.remote_ip4, dst=str(dst_addr))
589                 / UDP(sport=1234, dport=1234)
590                 / Raw(payload)
591             )
592             info.data = p.copy()
593             self.extend_packet(p, random.choice(self.pg_if_packet_sizes))
594             pkts.append(p)
595
596         return pkts
597
598     def _find_ip_match(self, find_in, pkt):
599         for p in find_in:
600             if self.payload_to_info(p[Raw]) == self.payload_to_info(pkt[Raw]):
601                 if p[IP].src != pkt[IP].src:
602                     break
603                 if p[IP].dst != pkt[IP].dst:
604                     break
605                 if p[UDP].sport != pkt[UDP].sport:
606                     break
607                 if p[UDP].dport != pkt[UDP].dport:
608                     break
609                 return p
610         return None
611
612     def verify_capture(self, dst_interface, received_pkts, expected_pkts):
613         self.assertEqual(len(received_pkts), len(expected_pkts))
614         to_verify = list(expected_pkts)
615         for p in received_pkts:
616             self.assertEqual(p.src, dst_interface.local_mac)
617             self.assertEqual(p.dst, dst_interface.remote_mac)
618             x = self._find_ip_match(to_verify, p)
619             to_verify.remove(x)
620         self.assertListEqual(to_verify, [])
621
622     def verify_route_dump(self, routes):
623         for r in routes:
624             self.assertTrue(
625                 find_route(self, r.prefix.network_address, r.prefix.prefixlen)
626             )
627
628     def verify_not_in_route_dump(self, routes):
629         for r in routes:
630             self.assertFalse(
631                 find_route(self, r.prefix.network_address, r.prefix.prefixlen)
632             )
633
634     @classmethod
635     def setUpClass(cls):
636         """
637         #. Create and initialize 3 pg interfaces.
638         #. initialize class attributes configured_routes and deleted_routes
639            to store information between tests.
640         """
641         super(TestIPv4FibCrud, cls).setUpClass()
642
643         try:
644             # create 3 pg interfaces
645             cls.create_pg_interfaces(range(3))
646
647             cls.interfaces = list(cls.pg_interfaces)
648
649             # setup all interfaces
650             for i in cls.interfaces:
651                 i.admin_up()
652                 i.config_ip4()
653                 i.resolve_arp()
654
655             cls.configured_routes = []
656             cls.deleted_routes = []
657             cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
658
659         except Exception:
660             super(TestIPv4FibCrud, cls).tearDownClass()
661             raise
662
663     @classmethod
664     def tearDownClass(cls):
665         super(TestIPv4FibCrud, cls).tearDownClass()
666
667     def setUp(self):
668         super(TestIPv4FibCrud, self).setUp()
669         self.reset_packet_infos()
670
671         self.configured_routes = []
672         self.deleted_routes = []
673
674     def test_1_add_routes(self):
675         """Add 1k routes"""
676
677         # add 100 routes check with traffic script.
678         self.configured_routes.extend(
679             self.config_fib_many_to_one("10.0.0.%d", self.pg0.remote_ip4, 100)
680         )
681
682         self.verify_route_dump(self.configured_routes)
683
684         self.stream_1 = self.create_stream(
685             self.pg1, self.pg0, self.configured_routes, 100
686         )
687         self.stream_2 = self.create_stream(
688             self.pg2, self.pg0, self.configured_routes, 100
689         )
690         self.pg1.add_stream(self.stream_1)
691         self.pg2.add_stream(self.stream_2)
692
693         self.pg_enable_capture(self.pg_interfaces)
694         self.pg_start()
695
696         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
697         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
698
699     def test_2_del_routes(self):
700         """Delete 100 routes
701
702         - delete 10 routes check with traffic script.
703         """
704         # config 1M FIB entries
705         self.configured_routes.extend(
706             self.config_fib_many_to_one("10.0.0.%d", self.pg0.remote_ip4, 100)
707         )
708         self.deleted_routes.extend(
709             self.unconfig_fib_many_to_one(
710                 "10.0.0.%d", self.pg0.remote_ip4, 10, start=10
711             )
712         )
713         for x in self.deleted_routes:
714             self.configured_routes.remove(x)
715
716         self.verify_route_dump(self.configured_routes)
717
718         self.stream_1 = self.create_stream(
719             self.pg1, self.pg0, self.configured_routes, 100
720         )
721         self.stream_2 = self.create_stream(
722             self.pg2, self.pg0, self.configured_routes, 100
723         )
724         self.stream_3 = self.create_stream(self.pg1, self.pg0, self.deleted_routes, 100)
725         self.stream_4 = self.create_stream(self.pg2, self.pg0, self.deleted_routes, 100)
726         self.pg1.add_stream(self.stream_1 + self.stream_3)
727         self.pg2.add_stream(self.stream_2 + self.stream_4)
728         self.pg_enable_capture(self.pg_interfaces)
729         self.pg_start()
730
731         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
732         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
733
734     def test_3_add_new_routes(self):
735         """Add 1k routes
736
737         - re-add 5 routes check with traffic script.
738         - add 100 routes check with traffic script.
739         """
740         # config 1M FIB entries
741         self.configured_routes.extend(
742             self.config_fib_many_to_one("10.0.0.%d", self.pg0.remote_ip4, 100)
743         )
744         self.deleted_routes.extend(
745             self.unconfig_fib_many_to_one(
746                 "10.0.0.%d", self.pg0.remote_ip4, 10, start=10
747             )
748         )
749         for x in self.deleted_routes:
750             self.configured_routes.remove(x)
751
752         tmp = self.config_fib_many_to_one("10.0.0.%d", self.pg0.remote_ip4, 5, start=10)
753         self.configured_routes.extend(tmp)
754         for x in tmp:
755             self.deleted_routes.remove(x)
756
757         self.configured_routes.extend(
758             self.config_fib_many_to_one("10.0.1.%d", self.pg0.remote_ip4, 100)
759         )
760
761         self.verify_route_dump(self.configured_routes)
762
763         self.stream_1 = self.create_stream(
764             self.pg1, self.pg0, self.configured_routes, 300
765         )
766         self.stream_2 = self.create_stream(
767             self.pg2, self.pg0, self.configured_routes, 300
768         )
769         self.stream_3 = self.create_stream(self.pg1, self.pg0, self.deleted_routes, 100)
770         self.stream_4 = self.create_stream(self.pg2, self.pg0, self.deleted_routes, 100)
771
772         self.pg1.add_stream(self.stream_1 + self.stream_3)
773         self.pg2.add_stream(self.stream_2 + self.stream_4)
774         self.pg_enable_capture(self.pg_interfaces)
775         self.pg_start()
776
777         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
778         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
779
780         # delete 5 routes check with traffic script.
781         # add 100 routes check with traffic script.
782         self.deleted_routes.extend(
783             self.unconfig_fib_many_to_one("10.0.0.%d", self.pg0.remote_ip4, 15)
784         )
785         self.deleted_routes.extend(
786             self.unconfig_fib_many_to_one("10.0.0.%d", self.pg0.remote_ip4, 85)
787         )
788         self.deleted_routes.extend(
789             self.unconfig_fib_many_to_one("10.0.1.%d", self.pg0.remote_ip4, 100)
790         )
791         self.verify_not_in_route_dump(self.deleted_routes)
792
793
794 class TestIPNull(VppTestCase):
795     """IPv4 routes via NULL"""
796
797     @classmethod
798     def setUpClass(cls):
799         super(TestIPNull, cls).setUpClass()
800
801     @classmethod
802     def tearDownClass(cls):
803         super(TestIPNull, cls).tearDownClass()
804
805     def setUp(self):
806         super(TestIPNull, self).setUp()
807
808         # create 2 pg interfaces
809         self.create_pg_interfaces(range(2))
810
811         for i in self.pg_interfaces:
812             i.admin_up()
813             i.config_ip4()
814             i.resolve_arp()
815
816     def tearDown(self):
817         super(TestIPNull, self).tearDown()
818         for i in self.pg_interfaces:
819             i.unconfig_ip4()
820             i.admin_down()
821
822     def test_ip_null(self):
823         """IP NULL route"""
824
825         #
826         # A route via IP NULL that will reply with ICMP unreachables
827         #
828         ip_unreach = VppIpRoute(
829             self,
830             "10.0.0.1",
831             32,
832             [
833                 VppRoutePath(
834                     "0.0.0.0", 0xFFFFFFFF, type=FibPathType.FIB_PATH_TYPE_ICMP_UNREACH
835                 )
836             ],
837         )
838         ip_unreach.add_vpp_config()
839
840         p_unreach = (
841             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
842             / IP(src=self.pg0.remote_ip4, dst="10.0.0.1")
843             / UDP(sport=1234, dport=1234)
844             / Raw(b"\xa5" * 100)
845         )
846         self.pg0.add_stream(p_unreach)
847         self.pg_enable_capture(self.pg_interfaces)
848         self.pg_start()
849
850         rx = self.pg0.get_capture(1)
851         rx = rx[0]
852         icmp = rx[ICMP]
853
854         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
855         self.assertEqual(icmpcodes[icmp.type][icmp.code], "host-unreachable")
856         self.assertEqual(icmp.src, self.pg0.remote_ip4)
857         self.assertEqual(icmp.dst, "10.0.0.1")
858
859         #
860         # ICMP replies are rate limited. so sit and spin.
861         #
862         self.sleep(1)
863
864         #
865         # A route via IP NULL that will reply with ICMP prohibited
866         #
867         ip_prohibit = VppIpRoute(
868             self,
869             "10.0.0.2",
870             32,
871             [
872                 VppRoutePath(
873                     "0.0.0.0", 0xFFFFFFFF, type=FibPathType.FIB_PATH_TYPE_ICMP_PROHIBIT
874                 )
875             ],
876         )
877         ip_prohibit.add_vpp_config()
878
879         p_prohibit = (
880             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
881             / IP(src=self.pg0.remote_ip4, dst="10.0.0.2")
882             / UDP(sport=1234, dport=1234)
883             / Raw(b"\xa5" * 100)
884         )
885
886         self.pg0.add_stream(p_prohibit)
887         self.pg_enable_capture(self.pg_interfaces)
888         self.pg_start()
889
890         rx = self.pg0.get_capture(1)
891
892         rx = rx[0]
893         icmp = rx[ICMP]
894
895         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
896         self.assertEqual(icmpcodes[icmp.type][icmp.code], "host-prohibited")
897         self.assertEqual(icmp.src, self.pg0.remote_ip4)
898         self.assertEqual(icmp.dst, "10.0.0.2")
899
900     def test_ip_drop(self):
901         """IP Drop Routes"""
902
903         p = (
904             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
905             / IP(src=self.pg0.remote_ip4, dst="1.1.1.1")
906             / UDP(sport=1234, dport=1234)
907             / Raw(b"\xa5" * 100)
908         )
909
910         r1 = VppIpRoute(
911             self,
912             "1.1.1.0",
913             24,
914             [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)],
915         )
916         r1.add_vpp_config()
917
918         rx = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg1)
919
920         #
921         # insert a more specific as a drop
922         #
923         r2 = VppIpRoute(
924             self,
925             "1.1.1.1",
926             32,
927             [VppRoutePath("0.0.0.0", 0xFFFFFFFF, type=FibPathType.FIB_PATH_TYPE_DROP)],
928         )
929         r2.add_vpp_config()
930
931         self.send_and_assert_no_replies(self.pg0, p * NUM_PKTS, "Drop Route")
932         r2.remove_vpp_config()
933         rx = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg1)
934
935
936 class TestIPDisabled(VppTestCase):
937     """IPv4 disabled"""
938
939     @classmethod
940     def setUpClass(cls):
941         super(TestIPDisabled, cls).setUpClass()
942
943     @classmethod
944     def tearDownClass(cls):
945         super(TestIPDisabled, cls).tearDownClass()
946
947     def setUp(self):
948         super(TestIPDisabled, self).setUp()
949
950         # create 2 pg interfaces
951         self.create_pg_interfaces(range(2))
952
953         # PG0 is IP enalbed
954         self.pg0.admin_up()
955         self.pg0.config_ip4()
956         self.pg0.resolve_arp()
957
958         # PG 1 is not IP enabled
959         self.pg1.admin_up()
960
961     def tearDown(self):
962         super(TestIPDisabled, self).tearDown()
963         for i in self.pg_interfaces:
964             i.unconfig_ip4()
965             i.admin_down()
966
967     def test_ip_disabled(self):
968         """IP Disabled"""
969
970         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
971         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
972
973         #
974         # An (S,G).
975         # one accepting interface, pg0, 2 forwarding interfaces
976         #
977         route_232_1_1_1 = VppIpMRoute(
978             self,
979             "0.0.0.0",
980             "232.1.1.1",
981             32,
982             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
983             [
984                 VppMRoutePath(
985                     self.pg1.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT
986                 ),
987                 VppMRoutePath(
988                     self.pg0.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD
989                 ),
990             ],
991         )
992         route_232_1_1_1.add_vpp_config()
993
994         pu = (
995             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
996             / IP(src="10.10.10.10", dst=self.pg0.remote_ip4)
997             / UDP(sport=1234, dport=1234)
998             / Raw(b"\xa5" * 100)
999         )
1000         pm = (
1001             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
1002             / IP(src="10.10.10.10", dst="232.1.1.1")
1003             / UDP(sport=1234, dport=1234)
1004             / Raw(b"\xa5" * 100)
1005         )
1006
1007         #
1008         # PG1 does not forward IP traffic
1009         #
1010         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
1011         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
1012
1013         #
1014         # IP enable PG1
1015         #
1016         self.pg1.config_ip4()
1017
1018         #
1019         # Now we get packets through
1020         #
1021         self.pg1.add_stream(pu)
1022         self.pg_enable_capture(self.pg_interfaces)
1023         self.pg_start()
1024         rx = self.pg0.get_capture(1)
1025
1026         self.pg1.add_stream(pm)
1027         self.pg_enable_capture(self.pg_interfaces)
1028         self.pg_start()
1029         rx = self.pg0.get_capture(1)
1030
1031         #
1032         # Disable PG1
1033         #
1034         self.pg1.unconfig_ip4()
1035
1036         #
1037         # PG1 does not forward IP traffic
1038         #
1039         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
1040         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
1041
1042
1043 class TestIPSubNets(VppTestCase):
1044     """IPv4 Subnets"""
1045
1046     @classmethod
1047     def setUpClass(cls):
1048         super(TestIPSubNets, cls).setUpClass()
1049
1050     @classmethod
1051     def tearDownClass(cls):
1052         super(TestIPSubNets, cls).tearDownClass()
1053
1054     def setUp(self):
1055         super(TestIPSubNets, self).setUp()
1056
1057         # create a 2 pg interfaces
1058         self.create_pg_interfaces(range(2))
1059
1060         # pg0 we will use to experiment
1061         self.pg0.admin_up()
1062
1063         # pg1 is setup normally
1064         self.pg1.admin_up()
1065         self.pg1.config_ip4()
1066         self.pg1.resolve_arp()
1067
1068     def tearDown(self):
1069         super(TestIPSubNets, self).tearDown()
1070         for i in self.pg_interfaces:
1071             i.admin_down()
1072
1073     def test_ip_sub_nets(self):
1074         """IP Sub Nets"""
1075
1076         #
1077         # Configure a covering route to forward so we know
1078         # when we are dropping
1079         #
1080         cover_route = VppIpRoute(
1081             self,
1082             "10.0.0.0",
1083             8,
1084             [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)],
1085         )
1086         cover_route.add_vpp_config()
1087
1088         p = (
1089             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
1090             / IP(dst="10.10.10.10", src=self.pg0.local_ip4)
1091             / UDP(sport=1234, dport=1234)
1092             / Raw(b"\xa5" * 100)
1093         )
1094
1095         self.pg1.add_stream(p)
1096         self.pg_enable_capture(self.pg_interfaces)
1097         self.pg_start()
1098         rx = self.pg1.get_capture(1)
1099
1100         #
1101         # Configure some non-/24 subnets on an IP interface
1102         #
1103         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
1104
1105         self.vapi.sw_interface_add_del_address(
1106             sw_if_index=self.pg0.sw_if_index, prefix="10.10.10.10/16"
1107         )
1108
1109         pn = (
1110             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
1111             / IP(dst="10.10.0.0", src=self.pg0.local_ip4)
1112             / UDP(sport=1234, dport=1234)
1113             / Raw(b"\xa5" * 100)
1114         )
1115         pb = (
1116             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
1117             / IP(dst="10.10.255.255", src=self.pg0.local_ip4)
1118             / UDP(sport=1234, dport=1234)
1119             / Raw(b"\xa5" * 100)
1120         )
1121
1122         self.send_and_assert_no_replies(self.pg1, pn, "IP Network address")
1123         self.send_and_assert_no_replies(self.pg1, pb, "IP Broadcast address")
1124
1125         # remove the sub-net and we are forwarding via the cover again
1126         self.vapi.sw_interface_add_del_address(
1127             sw_if_index=self.pg0.sw_if_index, prefix="10.10.10.10/16", is_add=0
1128         )
1129
1130         self.pg1.add_stream(pn)
1131         self.pg_enable_capture(self.pg_interfaces)
1132         self.pg_start()
1133         rx = self.pg1.get_capture(1)
1134         self.pg1.add_stream(pb)
1135         self.pg_enable_capture(self.pg_interfaces)
1136         self.pg_start()
1137         rx = self.pg1.get_capture(1)
1138
1139         #
1140         # A /31 is a special case where the 'other-side' is an attached host
1141         # packets to that peer generate ARP requests
1142         #
1143         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
1144
1145         self.vapi.sw_interface_add_del_address(
1146             sw_if_index=self.pg0.sw_if_index, prefix="10.10.10.10/31"
1147         )
1148
1149         pn = (
1150             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
1151             / IP(dst="10.10.10.11", src=self.pg0.local_ip4)
1152             / UDP(sport=1234, dport=1234)
1153             / Raw(b"\xa5" * 100)
1154         )
1155
1156         self.pg1.add_stream(pn)
1157         self.pg_enable_capture(self.pg_interfaces)
1158         self.pg_start()
1159         rx = self.pg0.get_capture(1)
1160         rx[ARP]
1161
1162         # remove the sub-net and we are forwarding via the cover again
1163         self.vapi.sw_interface_add_del_address(
1164             sw_if_index=self.pg0.sw_if_index, prefix="10.10.10.10/31", is_add=0
1165         )
1166
1167         self.pg1.add_stream(pn)
1168         self.pg_enable_capture(self.pg_interfaces)
1169         self.pg_start()
1170         rx = self.pg1.get_capture(1)
1171
1172
1173 class TestIPLoadBalance(VppTestCase):
1174     """IPv4 Load-Balancing"""
1175
1176     @classmethod
1177     def setUpClass(cls):
1178         super(TestIPLoadBalance, cls).setUpClass()
1179
1180     @classmethod
1181     def tearDownClass(cls):
1182         super(TestIPLoadBalance, cls).tearDownClass()
1183
1184     def setUp(self):
1185         super(TestIPLoadBalance, self).setUp()
1186
1187         self.create_pg_interfaces(range(5))
1188         mpls_tbl = VppMplsTable(self, 0)
1189         mpls_tbl.add_vpp_config()
1190
1191         for i in self.pg_interfaces:
1192             i.admin_up()
1193             i.config_ip4()
1194             i.resolve_arp()
1195             i.enable_mpls()
1196
1197     def tearDown(self):
1198         for i in self.pg_interfaces:
1199             i.disable_mpls()
1200             i.unconfig_ip4()
1201             i.admin_down()
1202         super(TestIPLoadBalance, self).tearDown()
1203
1204     def total_len(self, rxs):
1205         n = 0
1206         for rx in rxs:
1207             n += len(rx)
1208         return n
1209
1210     def test_ip_load_balance(self):
1211         """IP Load-Balancing"""
1212
1213         fhc = VppEnum.vl_api_ip_flow_hash_config_t
1214         fhcv2 = VppEnum.vl_api_ip_flow_hash_config_v2_t
1215         af = VppEnum.vl_api_address_family_t
1216
1217         #
1218         # An array of packets that differ only in the destination port
1219         #
1220         port_ip_pkts = []
1221         port_mpls_pkts = []
1222         port_gtp_pkts = []
1223
1224         #
1225         # An array of packets that differ only in the source address
1226         #
1227         src_ip_pkts = []
1228         src_mpls_pkts = []
1229         src_gtp_pkts = []
1230
1231         for ii in range(NUM_PKTS):
1232             internal_src_ip_hdr = IP(dst="10.0.0.1", src="20.0.0.1")
1233
1234             port_ip_hdr = (
1235                 internal_src_ip_hdr
1236                 / UDP(sport=1234, dport=1234 + ii)
1237                 / Raw(b"\xa5" * 100)
1238             )
1239             port_ip_pkts.append(
1240                 (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / port_ip_hdr)
1241             )
1242             port_mpls_pkts.append(
1243                 (
1244                     Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1245                     / MPLS(label=66, ttl=2)
1246                     / port_ip_hdr
1247                 )
1248             )
1249             port_gtp_pkts.append(
1250                 (
1251                     Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1252                     / internal_src_ip_hdr
1253                     / UDP(sport=2152, dport=2152, chksum=0)
1254                     / GTP_U_Header(gtp_type="g_pdu", teid=200)
1255                     / Raw(b"\xa5" * 100)
1256                 )
1257             )
1258
1259             src_ip_hdr = (
1260                 IP(dst="10.0.0.1", src="20.0.0.%d" % ii)
1261                 / UDP(sport=1234, dport=1234)
1262                 / Raw(b"\xa5" * 100)
1263             )
1264             src_ip_pkts.append(
1265                 (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / src_ip_hdr)
1266             )
1267             src_mpls_pkts.append(
1268                 (
1269                     Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1270                     / MPLS(label=66, ttl=2)
1271                     / src_ip_hdr
1272                 )
1273             )
1274             src_gtp_pkts.append(
1275                 (
1276                     Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1277                     / IP(dst="10.0.0.1", src="20.0.0.1")
1278                     / UDP(sport=2152, dport=2152, chksum=0)
1279                     / GTP_U_Header(gtp_type="g_pdu", teid=ii)
1280                     / Raw(b"\xa5" * 100)
1281                 )
1282             )
1283
1284         route_10_0_0_1 = VppIpRoute(
1285             self,
1286             "10.0.0.1",
1287             32,
1288             [
1289                 VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index),
1290                 VppRoutePath(self.pg2.remote_ip4, self.pg2.sw_if_index),
1291             ],
1292         )
1293         route_10_0_0_1.add_vpp_config()
1294
1295         binding = VppMplsIpBind(self, 66, "10.0.0.1", 32)
1296         binding.add_vpp_config()
1297
1298         #
1299         # inject the packet on pg0 - expect load-balancing across the 2 paths
1300         #  - since the default hash config is to use IP src,dst and port
1301         #    src,dst
1302         # We are not going to ensure equal amounts of packets across each link,
1303         # since the hash algorithm is statistical and therefore this can never
1304         # be guaranteed. But with 64 different packets we do expect some
1305         # balancing. So instead just ensure there is traffic on each link.
1306         #
1307         rx = self.send_and_expect_load_balancing(
1308             self.pg0, port_ip_pkts, [self.pg1, self.pg2]
1309         )
1310         n_ip_pg0 = len(rx[0])
1311         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts, [self.pg1, self.pg2])
1312         self.send_and_expect_load_balancing(
1313             self.pg0, port_mpls_pkts, [self.pg1, self.pg2]
1314         )
1315         rx = self.send_and_expect_load_balancing(
1316             self.pg0, src_mpls_pkts, [self.pg1, self.pg2]
1317         )
1318         n_mpls_pg0 = len(rx[0])
1319
1320         #
1321         # change the router ID and expect the distribution changes
1322         #
1323         self.vapi.set_ip_flow_hash_router_id(router_id=0x11111111)
1324
1325         rx = self.send_and_expect_load_balancing(
1326             self.pg0, port_ip_pkts, [self.pg1, self.pg2]
1327         )
1328         self.assertNotEqual(n_ip_pg0, len(rx[0]))
1329
1330         rx = self.send_and_expect_load_balancing(
1331             self.pg0, src_mpls_pkts, [self.pg1, self.pg2]
1332         )
1333         self.assertNotEqual(n_mpls_pg0, len(rx[0]))
1334
1335         #
1336         # change the flow hash config so it's only IP src,dst
1337         #  - now only the stream with differing source address will
1338         #    load-balance
1339         #
1340         self.vapi.set_ip_flow_hash_v2(
1341             af=af.ADDRESS_IP4,
1342             table_id=0,
1343             flow_hash_config=(
1344                 fhc.IP_API_FLOW_HASH_SRC_IP
1345                 | fhc.IP_API_FLOW_HASH_DST_IP
1346                 | fhc.IP_API_FLOW_HASH_PROTO
1347             ),
1348         )
1349
1350         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts, [self.pg1, self.pg2])
1351         self.send_and_expect_load_balancing(
1352             self.pg0, src_mpls_pkts, [self.pg1, self.pg2]
1353         )
1354
1355         self.send_and_expect_only(self.pg0, port_ip_pkts, self.pg2)
1356
1357         #
1358         # this case gtp v1 teid key LB
1359         #
1360         self.vapi.set_ip_flow_hash_v3(
1361             af=af.ADDRESS_IP4,
1362             table_id=0,
1363             flow_hash_config=(
1364                 fhcv2.IP_API_V2_FLOW_HASH_SRC_IP
1365                 | fhcv2.IP_API_V2_FLOW_HASH_PROTO
1366                 | fhcv2.IP_API_V2_FLOW_HASH_GTPV1_TEID
1367             ),
1368         )
1369         self.logger.info(self.vapi.cli("show ip fib"))
1370
1371         self.send_and_expect_load_balancing(
1372             self.pg0, src_gtp_pkts, [self.pg1, self.pg2]
1373         )
1374
1375         self.send_and_expect_only(self.pg0, port_gtp_pkts, self.pg2)
1376
1377         #
1378         # change the flow hash config back to defaults
1379         #
1380         self.vapi.set_ip_flow_hash(vrf_id=0, src=1, dst=1, proto=1, sport=1, dport=1)
1381
1382         #
1383         # Recursive prefixes
1384         #  - testing that 2 stages of load-balancing occurs and there is no
1385         #    polarisation (i.e. only 2 of 4 paths are used)
1386         #
1387         port_pkts = []
1388         src_pkts = []
1389
1390         for ii in range(257):
1391             port_pkts.append(
1392                 (
1393                     Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1394                     / IP(dst="1.1.1.1", src="20.0.0.1")
1395                     / UDP(sport=1234, dport=1234 + ii)
1396                     / Raw(b"\xa5" * 100)
1397                 )
1398             )
1399             src_pkts.append(
1400                 (
1401                     Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1402                     / IP(dst="1.1.1.1", src="20.0.0.%d" % ii)
1403                     / UDP(sport=1234, dport=1234)
1404                     / Raw(b"\xa5" * 100)
1405                 )
1406             )
1407
1408         route_10_0_0_2 = VppIpRoute(
1409             self,
1410             "10.0.0.2",
1411             32,
1412             [
1413                 VppRoutePath(self.pg3.remote_ip4, self.pg3.sw_if_index),
1414                 VppRoutePath(self.pg4.remote_ip4, self.pg4.sw_if_index),
1415             ],
1416         )
1417         route_10_0_0_2.add_vpp_config()
1418
1419         route_1_1_1_1 = VppIpRoute(
1420             self,
1421             "1.1.1.1",
1422             32,
1423             [
1424                 VppRoutePath("10.0.0.2", 0xFFFFFFFF),
1425                 VppRoutePath("10.0.0.1", 0xFFFFFFFF),
1426             ],
1427         )
1428         route_1_1_1_1.add_vpp_config()
1429
1430         #
1431         # inject the packet on pg0 - expect load-balancing across all 4 paths
1432         #
1433         self.vapi.cli("clear trace")
1434         self.send_and_expect_load_balancing(
1435             self.pg0, port_pkts, [self.pg1, self.pg2, self.pg3, self.pg4]
1436         )
1437         self.send_and_expect_load_balancing(
1438             self.pg0, src_pkts, [self.pg1, self.pg2, self.pg3, self.pg4]
1439         )
1440
1441         #
1442         # bring down pg1 expect LB to adjust to use only those that are up
1443         #
1444         self.pg1.link_down()
1445
1446         rx = self.send_and_expect_load_balancing(
1447             self.pg0, src_pkts, [self.pg2, self.pg3, self.pg4]
1448         )
1449         self.assertEqual(len(src_pkts), self.total_len(rx))
1450
1451         #
1452         # bring down pg2 expect LB to adjust to use only those that are up
1453         #
1454         self.pg2.link_down()
1455
1456         rx = self.send_and_expect_load_balancing(
1457             self.pg0, src_pkts, [self.pg3, self.pg4]
1458         )
1459         self.assertEqual(len(src_pkts), self.total_len(rx))
1460
1461         #
1462         # bring the links back up - expect LB over all again
1463         #
1464         self.pg1.link_up()
1465         self.pg2.link_up()
1466
1467         rx = self.send_and_expect_load_balancing(
1468             self.pg0, src_pkts, [self.pg1, self.pg2, self.pg3, self.pg4]
1469         )
1470         self.assertEqual(len(src_pkts), self.total_len(rx))
1471
1472         #
1473         # The same link-up/down but this time admin state
1474         #
1475         self.pg1.admin_down()
1476         self.pg2.admin_down()
1477         rx = self.send_and_expect_load_balancing(
1478             self.pg0, src_pkts, [self.pg3, self.pg4]
1479         )
1480         self.assertEqual(len(src_pkts), self.total_len(rx))
1481         self.pg1.admin_up()
1482         self.pg2.admin_up()
1483         self.pg1.resolve_arp()
1484         self.pg2.resolve_arp()
1485         rx = self.send_and_expect_load_balancing(
1486             self.pg0, src_pkts, [self.pg1, self.pg2, self.pg3, self.pg4]
1487         )
1488         self.assertEqual(len(src_pkts), self.total_len(rx))
1489
1490         #
1491         # Recursive prefixes
1492         #  - testing that 2 stages of load-balancing, no choices
1493         #
1494         port_pkts = []
1495
1496         for ii in range(257):
1497             port_pkts.append(
1498                 (
1499                     Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1500                     / IP(dst="1.1.1.2", src="20.0.0.2")
1501                     / UDP(sport=1234, dport=1234 + ii)
1502                     / Raw(b"\xa5" * 100)
1503                 )
1504             )
1505
1506         route_10_0_0_3 = VppIpRoute(
1507             self,
1508             "10.0.0.3",
1509             32,
1510             [VppRoutePath(self.pg3.remote_ip4, self.pg3.sw_if_index)],
1511         )
1512         route_10_0_0_3.add_vpp_config()
1513
1514         route_1_1_1_2 = VppIpRoute(
1515             self, "1.1.1.2", 32, [VppRoutePath("10.0.0.3", 0xFFFFFFFF)]
1516         )
1517         route_1_1_1_2.add_vpp_config()
1518
1519         #
1520         # inject the packet on pg0 - rx only on via routes output interface
1521         #
1522         self.vapi.cli("clear trace")
1523         self.send_and_expect_only(self.pg0, port_pkts, self.pg3)
1524
1525         #
1526         # Add a LB route in the presence of a down link - expect no
1527         # packets over the down link
1528         #
1529         self.pg3.link_down()
1530
1531         route_10_0_0_3 = VppIpRoute(
1532             self,
1533             "10.0.0.3",
1534             32,
1535             [
1536                 VppRoutePath(self.pg3.remote_ip4, self.pg3.sw_if_index),
1537                 VppRoutePath(self.pg4.remote_ip4, self.pg4.sw_if_index),
1538             ],
1539         )
1540         route_10_0_0_3.add_vpp_config()
1541
1542         port_pkts = []
1543         for ii in range(257):
1544             port_pkts.append(
1545                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1546                 / IP(dst="10.0.0.3", src="20.0.0.2")
1547                 / UDP(sport=1234, dport=1234 + ii)
1548                 / Raw(b"\xa5" * 100)
1549             )
1550
1551         self.send_and_expect_only(self.pg0, port_pkts, self.pg4)
1552
1553         # bring the link back up
1554         self.pg3.link_up()
1555
1556         rx = self.send_and_expect_load_balancing(
1557             self.pg0, port_pkts, [self.pg3, self.pg4]
1558         )
1559         self.assertEqual(len(src_pkts), self.total_len(rx))
1560
1561
1562 class TestIPVlan0(VppTestCase):
1563     """IPv4 VLAN-0"""
1564
1565     @classmethod
1566     def setUpClass(cls):
1567         super(TestIPVlan0, cls).setUpClass()
1568
1569     @classmethod
1570     def tearDownClass(cls):
1571         super(TestIPVlan0, cls).tearDownClass()
1572
1573     def setUp(self):
1574         super(TestIPVlan0, self).setUp()
1575
1576         self.create_pg_interfaces(range(2))
1577         mpls_tbl = VppMplsTable(self, 0)
1578         mpls_tbl.add_vpp_config()
1579
1580         for i in self.pg_interfaces:
1581             i.admin_up()
1582             i.config_ip4()
1583             i.resolve_arp()
1584             i.enable_mpls()
1585
1586     def tearDown(self):
1587         for i in self.pg_interfaces:
1588             i.disable_mpls()
1589             i.unconfig_ip4()
1590             i.admin_down()
1591         super(TestIPVlan0, self).tearDown()
1592
1593     def test_ip_vlan_0(self):
1594         """IP VLAN-0"""
1595
1596         pkts = (
1597             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1598             / Dot1Q(vlan=0)
1599             / IP(dst=self.pg1.remote_ip4, src=self.pg0.remote_ip4)
1600             / UDP(sport=1234, dport=1234)
1601             / Raw(b"\xa5" * 100)
1602         ) * NUM_PKTS
1603
1604         #
1605         # Expect that packets sent on VLAN-0 are forwarded on the
1606         # main interface.
1607         #
1608         self.send_and_expect(self.pg0, pkts, self.pg1)
1609
1610
1611 class IPPuntSetup(object):
1612     """Setup for IPv4 Punt Police/Redirect"""
1613
1614     def punt_setup(self):
1615         self.create_pg_interfaces(range(4))
1616
1617         for i in self.pg_interfaces:
1618             i.admin_up()
1619             i.config_ip4()
1620             i.resolve_arp()
1621
1622         # use UDP packet that have a port we need to explicitly
1623         # register to get punted.
1624         pt_l4 = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4
1625         af_ip4 = VppEnum.vl_api_address_family_t.ADDRESS_IP4
1626         udp_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
1627         punt_udp = {
1628             "type": pt_l4,
1629             "punt": {
1630                 "l4": {
1631                     "af": af_ip4,
1632                     "protocol": udp_proto,
1633                     "port": 1234,
1634                 }
1635             },
1636         }
1637
1638         self.vapi.set_punt(is_add=1, punt=punt_udp)
1639
1640         af_ip6 = VppEnum.vl_api_address_family_t.ADDRESS_IP6
1641         punt_udp = {
1642             "type": pt_l4,
1643             "punt": {
1644                 "l4": {
1645                     "af": af_ip6,
1646                     "protocol": udp_proto,
1647                     "port": 1236,
1648                 }
1649             },
1650         }
1651
1652         self.vapi.set_punt(is_add=1, punt=punt_udp)
1653
1654         self.pkt = (
1655             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1656             / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
1657             / UDP(sport=1234, dport=1234)
1658             / Raw(b"\xa5" * 100)
1659         )
1660
1661     def punt_teardown(self):
1662         for i in self.pg_interfaces:
1663             i.unconfig_ip4()
1664             i.admin_down()
1665
1666
1667 class TestIPPunt(IPPuntSetup, VppTestCase):
1668     """IPv4 Punt Police/Redirect"""
1669
1670     def setUp(self):
1671         super().setUp()
1672         super().punt_setup()
1673
1674     def tearDown(self):
1675         super().punt_teardown()
1676         super().tearDown()
1677
1678     def test_ip_punt_api_validation(self):
1679         """IP punt API parameter validation"""
1680
1681         nh_addr = self.pg1.remote_ip4
1682         punt = {
1683             "rx_sw_if_index": self.pg0.sw_if_index,
1684             "af": VppEnum.vl_api_address_family_t.ADDRESS_IP4,
1685             "n_paths": 1000000,
1686             "paths": [],
1687         }
1688
1689         with self.assertRaises(vpp_papi.VPPIOError):
1690             self.vapi.add_del_ip_punt_redirect_v2(punt=punt, is_add=True)
1691
1692         punt = {
1693             "rx_sw_if_index": self.pg0.sw_if_index,
1694             "af": VppEnum.vl_api_address_family_t.ADDRESS_IP4,
1695             "n_paths": 0,
1696             "paths": [],
1697         }
1698
1699         self.vapi.add_del_ip_punt_redirect_v2(punt=punt, is_add=True)
1700
1701     def test_ip_punt(self):
1702         """IP punt police and redirect"""
1703
1704         pkts = self.pkt * 1025
1705
1706         #
1707         # Configure a punt redirect via pg1.
1708         #
1709         nh_addr = self.pg1.remote_ip4
1710         ip_punt_redirect = VppIpPuntRedirect(
1711             self, self.pg0.sw_if_index, self.pg1.sw_if_index, nh_addr
1712         )
1713         ip_punt_redirect.add_vpp_config()
1714
1715         self.send_and_expect(self.pg0, pkts, self.pg1)
1716
1717         #
1718         # add a policer
1719         #
1720         policer = VppPolicer(self, "ip4-punt", 400, 0, 10, 0, rate_type=1)
1721         policer.add_vpp_config()
1722         ip_punt_policer = VppIpPuntPolicer(self, policer.policer_index)
1723         ip_punt_policer.add_vpp_config()
1724
1725         self.vapi.cli("clear trace")
1726         self.pg0.add_stream(pkts)
1727         self.pg_enable_capture(self.pg_interfaces)
1728         self.pg_start()
1729
1730         #
1731         # the number of packet received should be greater than 0,
1732         # but not equal to the number sent, since some were policed
1733         #
1734         rx = self.pg1._get_capture(1)
1735
1736         stats = policer.get_stats()
1737
1738         # Single rate policer - expect conform, violate but no exceed
1739         self.assertGreater(stats["conform_packets"], 0)
1740         self.assertEqual(stats["exceed_packets"], 0)
1741         self.assertGreater(stats["violate_packets"], 0)
1742
1743         self.assertGreater(len(rx), 0)
1744         self.assertLess(len(rx), len(pkts))
1745
1746         #
1747         # remove the policer. back to full rx
1748         #
1749         ip_punt_policer.remove_vpp_config()
1750         policer.remove_vpp_config()
1751         self.send_and_expect(self.pg0, pkts, self.pg1)
1752
1753         #
1754         # remove the redirect. expect full drop.
1755         #
1756         ip_punt_redirect.remove_vpp_config()
1757         self.send_and_assert_no_replies(self.pg0, pkts, "IP no punt config")
1758
1759         #
1760         # Add a redirect that is not input port selective
1761         #
1762         ip_punt_redirect = VppIpPuntRedirect(
1763             self, 0xFFFFFFFF, self.pg1.sw_if_index, nh_addr
1764         )
1765         ip_punt_redirect.add_vpp_config()
1766         self.send_and_expect(self.pg0, pkts, self.pg1)
1767         ip_punt_redirect.remove_vpp_config()
1768
1769     def test_ip_punt_vrf(self):
1770         """IP punt/local with VRFs"""
1771
1772         # use a punt redirect to test if for-us  packets are accepted
1773         pkts = self.pkt * 1025
1774
1775         vlans_pg0 = [VppDot1QSubint(self, self.pg0, v) for v in range(100, 104)]
1776         vlans_pg1 = [VppDot1QSubint(self, self.pg1, v) for v in range(100, 104)]
1777         tbl4 = [VppIpTable(self, v).add_vpp_config() for v in range(100, 104)]
1778         tbl6 = [VppIpTable(self, v, True).add_vpp_config() for v in range(100, 104)]
1779
1780         for v in vlans_pg0 + vlans_pg1:
1781             v.admin_up()
1782             v.set_table_ip4(v.vlan)
1783             v.set_table_ip6(v.vlan)
1784             v.config_ip4()
1785             v.config_ip6()
1786             v.resolve_arp()
1787             v.resolve_ndp()
1788
1789         [
1790             VppIpPuntRedirect(
1791                 self,
1792                 vlans_pg0[i].sw_if_index,
1793                 vlans_pg1[i].sw_if_index,
1794                 vlans_pg1[i].remote_ip4,
1795             ).add_vpp_config()
1796             for i in range(4)
1797         ]
1798         [
1799             VppIpPuntRedirect(
1800                 self,
1801                 vlans_pg0[i].sw_if_index,
1802                 vlans_pg1[i].sw_if_index,
1803                 vlans_pg1[i].remote_ip6,
1804             ).add_vpp_config()
1805             for i in range(4)
1806         ]
1807
1808         pkts = [
1809             (
1810                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1811                 / Dot1Q(vlan=i.vlan)
1812                 / IP(src=i.remote_ip4, dst=i.local_ip4)
1813                 / UDP(sport=1234, dport=1234)
1814                 / Raw(b"\xa5" * 100)
1815             )
1816             for i in vlans_pg0
1817         ]
1818
1819         self.send_and_expect(self.pg0, pkts, self.pg1)
1820
1821         #
1822         # IPv4
1823         #
1824
1825         # we reject packets for source addresses in the wrong vlan/VRF
1826         pkts = [
1827             (
1828                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1829                 / Dot1Q(vlan=i.vlan)
1830                 / IP(src="1.1.1.1", dst=i.local_ip4)
1831                 / UDP(sport=1234, dport=1234)
1832                 / Raw(b"\xa5" * 100)
1833             )
1834             for i in vlans_pg0
1835         ]
1836         # single and dual loop
1837         self.send_and_assert_no_replies(self.pg0, [pkts[0]])
1838         self.send_and_assert_no_replies(self.pg0, pkts)
1839
1840         self.assert_error_counter_equal("/err/ip4-local/src_lookup_miss", len(pkts) + 1)
1841
1842         # using the same source in different tables, should reject
1843         # for the table that the source is not present in
1844         # the first packet in the stream is drop
1845         pkts = [
1846             (
1847                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1848                 / Dot1Q(vlan=i.vlan)
1849                 / IP(src=vlans_pg0[0].remote_ip4, dst=i.local_ip4)
1850                 / UDP(sport=1234, dport=1234)
1851                 / Raw(b"\xa5" * 100)
1852             )
1853             for i in vlans_pg0
1854         ]
1855         # single loop accept and drop
1856         # followed by both in the same frame/loop
1857         self.send_and_expect(self.pg0, [pkts[0]], self.pg1)
1858         self.send_and_assert_no_replies(self.pg0, [pkts[1]])
1859         self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
1860
1861         # using the same source in different tables, should reject
1862         # for the table that the source is not present in
1863         # the first packet in the stream is accept
1864         pkts = [
1865             (
1866                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1867                 / Dot1Q(vlan=i.vlan)
1868                 / IP(src=vlans_pg0[3].remote_ip4, dst=i.local_ip4)
1869                 / UDP(sport=1234, dport=1234)
1870                 / Raw(b"\xa5" * 100)
1871             )
1872             for i in vlans_pg0
1873         ]
1874
1875         # single loop accept and drop
1876         # followed by both in the same frame/loop
1877         self.send_and_expect(self.pg0, [pkts[3]], self.pg1)
1878         self.send_and_assert_no_replies(self.pg0, [pkts[1]])
1879         self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
1880
1881         #
1882         # IPv6
1883         #
1884
1885         # we reject packets for source addresses in the wrong vlan/VRF
1886         pkts = [
1887             (
1888                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1889                 / Dot1Q(vlan=i.vlan)
1890                 / IPv6(src="1::1", dst=i.local_ip6)
1891                 / UDP(sport=1236, dport=1236)
1892                 / Raw(b"\xa5" * 100)
1893             )
1894             for i in vlans_pg0
1895         ]
1896         # single and dual loop
1897         self.send_and_assert_no_replies(self.pg0, [pkts[0]])
1898         self.send_and_assert_no_replies(self.pg0, pkts)
1899
1900         self.assert_error_counter_equal("/err/ip6-input/src_lookup_miss", len(pkts) + 1)
1901
1902         # using the same source in different tables, should reject
1903         # for the table that the source is not present in
1904         # the first packet in the stream is drop
1905         pkts = [
1906             (
1907                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1908                 / Dot1Q(vlan=i.vlan)
1909                 / IPv6(src=vlans_pg0[0].remote_ip6, dst=i.local_ip6)
1910                 / UDP(sport=1236, dport=1236)
1911                 / Raw(b"\xa5" * 100)
1912             )
1913             for i in vlans_pg0
1914         ]
1915         # single loop accept and drop
1916         # followed by both in the same frame/loop
1917         self.send_and_expect(self.pg0, [pkts[0]], self.pg1)
1918         self.send_and_assert_no_replies(self.pg0, [pkts[1]])
1919         self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
1920
1921         # using the same source in different tables, should reject
1922         # for the table that the source is not present in
1923         # the first packet in the stream is accept
1924         pkts = [
1925             (
1926                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1927                 / Dot1Q(vlan=i.vlan)
1928                 / IPv6(src=vlans_pg0[3].remote_ip6, dst=i.local_ip6)
1929                 / UDP(sport=1236, dport=1236)
1930                 / Raw(b"\xa5" * 100)
1931             )
1932             for i in vlans_pg0
1933         ]
1934
1935         # single loop accept and drop
1936         # followed by both in the same frame/loop
1937         self.send_and_expect(self.pg0, [pkts[3]], self.pg1)
1938         self.send_and_assert_no_replies(self.pg0, [pkts[1]])
1939         self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
1940
1941         for v in vlans_pg0 + vlans_pg1:
1942             v.unconfig_ip4()
1943             v.unconfig_ip6()
1944             v.set_table_ip4(0)
1945             v.set_table_ip6(0)
1946
1947     def test_ip_punt_dump(self):
1948         """IP4 punt redirect dump"""
1949
1950         #
1951         # Configure a punt redirects
1952         #
1953         nh_address = self.pg3.remote_ip4
1954         ipr_03 = VppIpPuntRedirect(
1955             self, self.pg0.sw_if_index, self.pg3.sw_if_index, nh_address
1956         )
1957         ipr_13 = VppIpPuntRedirect(
1958             self, self.pg1.sw_if_index, self.pg3.sw_if_index, nh_address
1959         )
1960         ipr_23 = VppIpPuntRedirect(
1961             self, self.pg2.sw_if_index, self.pg3.sw_if_index, "0.0.0.0"
1962         )
1963         ipr_03.add_vpp_config()
1964         ipr_13.add_vpp_config()
1965         ipr_23.add_vpp_config()
1966
1967         #
1968         # Dump pg0 punt redirects
1969         #
1970         self.assertTrue(ipr_03.query_vpp_config())
1971         self.assertTrue(ipr_13.query_vpp_config())
1972         self.assertTrue(ipr_23.query_vpp_config())
1973
1974         #
1975         # Dump punt redirects for all interfaces
1976         #
1977         punts = self.vapi.ip_punt_redirect_dump(0xFFFFFFFF)
1978         self.assertEqual(len(punts), 3)
1979         for p in punts:
1980             self.assertEqual(p.punt.tx_sw_if_index, self.pg3.sw_if_index)
1981         self.assertNotEqual(punts[1].punt.nh, self.pg3.remote_ip4)
1982         self.assertEqual(str(punts[2].punt.nh), "0.0.0.0")
1983
1984
1985 class TestIPPuntHandoff(IPPuntSetup, VppTestCase):
1986     """IPv4 Punt Policer thread handoff"""
1987
1988     vpp_worker_count = 2
1989
1990     def setUp(self):
1991         super(TestIPPuntHandoff, self).setUp()
1992         super(TestIPPuntHandoff, self).punt_setup()
1993
1994     def tearDown(self):
1995         super(TestIPPuntHandoff, self).punt_teardown()
1996         super(TestIPPuntHandoff, self).tearDown()
1997
1998     def test_ip_punt_policer_handoff(self):
1999         """IP4 punt policer thread handoff"""
2000         pkts = self.pkt * NUM_PKTS
2001
2002         #
2003         # Configure a punt redirect via pg1.
2004         #
2005         nh_addr = self.pg1.remote_ip4
2006         ip_punt_redirect = VppIpPuntRedirect(
2007             self, self.pg0.sw_if_index, self.pg1.sw_if_index, nh_addr
2008         )
2009         ip_punt_redirect.add_vpp_config()
2010
2011         action_tx = PolicerAction(
2012             VppEnum.vl_api_sse2_qos_action_type_t.SSE2_QOS_ACTION_API_TRANSMIT, 0
2013         )
2014         #
2015         # This policer drops no packets, we are just
2016         # testing that they get to the right thread.
2017         #
2018         policer = VppPolicer(
2019             self,
2020             "ip4-punt",
2021             400,
2022             0,
2023             10,
2024             0,
2025             1,
2026             0,
2027             0,
2028             False,
2029             action_tx,
2030             action_tx,
2031             action_tx,
2032         )
2033         policer.add_vpp_config()
2034         ip_punt_policer = VppIpPuntPolicer(self, policer.policer_index)
2035         ip_punt_policer.add_vpp_config()
2036
2037         for worker in [0, 1]:
2038             self.send_and_expect(self.pg0, pkts, self.pg1, worker=worker)
2039             self.logger.debug(self.vapi.cli("show trace max 100"))
2040
2041         # Combined stats, all threads
2042         stats = policer.get_stats()
2043
2044         # Single rate policer - expect conform, violate but no exceed
2045         self.assertGreater(stats["conform_packets"], 0)
2046         self.assertEqual(stats["exceed_packets"], 0)
2047         self.assertGreater(stats["violate_packets"], 0)
2048
2049         # Worker 0, should have done all the policing
2050         stats0 = policer.get_stats(worker=0)
2051         self.assertEqual(stats, stats0)
2052
2053         # Worker 1, should have handed everything off
2054         stats1 = policer.get_stats(worker=1)
2055         self.assertEqual(stats1["conform_packets"], 0)
2056         self.assertEqual(stats1["exceed_packets"], 0)
2057         self.assertEqual(stats1["violate_packets"], 0)
2058
2059         # Bind the policer to worker 1 and repeat
2060         policer.bind_vpp_config(1, True)
2061         for worker in [0, 1]:
2062             self.send_and_expect(self.pg0, pkts, self.pg1, worker=worker)
2063             self.logger.debug(self.vapi.cli("show trace max 100"))
2064
2065         # The 2 workers should now have policed the same amount
2066         stats = policer.get_stats()
2067         stats0 = policer.get_stats(worker=0)
2068         stats1 = policer.get_stats(worker=1)
2069
2070         self.assertGreater(stats0["conform_packets"], 0)
2071         self.assertEqual(stats0["exceed_packets"], 0)
2072         self.assertGreater(stats0["violate_packets"], 0)
2073
2074         self.assertGreater(stats1["conform_packets"], 0)
2075         self.assertEqual(stats1["exceed_packets"], 0)
2076         self.assertGreater(stats1["violate_packets"], 0)
2077
2078         self.assertEqual(
2079             stats0["conform_packets"] + stats1["conform_packets"],
2080             stats["conform_packets"],
2081         )
2082
2083         self.assertEqual(
2084             stats0["violate_packets"] + stats1["violate_packets"],
2085             stats["violate_packets"],
2086         )
2087
2088         # Unbind the policer and repeat
2089         policer.bind_vpp_config(1, False)
2090         for worker in [0, 1]:
2091             self.send_and_expect(self.pg0, pkts, self.pg1, worker=worker)
2092             self.logger.debug(self.vapi.cli("show trace max 100"))
2093
2094         # The policer should auto-bind to worker 0 when packets arrive
2095         stats = policer.get_stats()
2096         stats0new = policer.get_stats(worker=0)
2097         stats1new = policer.get_stats(worker=1)
2098
2099         self.assertGreater(stats0new["conform_packets"], stats0["conform_packets"])
2100         self.assertEqual(stats0new["exceed_packets"], 0)
2101         self.assertGreater(stats0new["violate_packets"], stats0["violate_packets"])
2102
2103         self.assertEqual(stats1, stats1new)
2104
2105         #
2106         # Clean up
2107         #
2108         ip_punt_policer.remove_vpp_config()
2109         policer.remove_vpp_config()
2110         ip_punt_redirect.remove_vpp_config()
2111
2112
2113 class TestIPDeag(VppTestCase):
2114     """IPv4 Deaggregate Routes"""
2115
2116     @classmethod
2117     def setUpClass(cls):
2118         super(TestIPDeag, cls).setUpClass()
2119
2120     @classmethod
2121     def tearDownClass(cls):
2122         super(TestIPDeag, cls).tearDownClass()
2123
2124     def setUp(self):
2125         super(TestIPDeag, self).setUp()
2126
2127         self.create_pg_interfaces(range(3))
2128
2129         for i in self.pg_interfaces:
2130             i.admin_up()
2131             i.config_ip4()
2132             i.resolve_arp()
2133
2134     def tearDown(self):
2135         super(TestIPDeag, self).tearDown()
2136         for i in self.pg_interfaces:
2137             i.unconfig_ip4()
2138             i.admin_down()
2139
2140     def test_ip_deag(self):
2141         """IP Deag Routes"""
2142
2143         #
2144         # Create a table to be used for:
2145         #  1 - another destination address lookup
2146         #  2 - a source address lookup
2147         #
2148         table_dst = VppIpTable(self, 1)
2149         table_src = VppIpTable(self, 2)
2150         table_dst.add_vpp_config()
2151         table_src.add_vpp_config()
2152
2153         #
2154         # Add a route in the default table to point to a deag/
2155         # second lookup in each of these tables
2156         #
2157         route_to_dst = VppIpRoute(
2158             self, "1.1.1.1", 32, [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=1)]
2159         )
2160         route_to_src = VppIpRoute(
2161             self,
2162             "1.1.1.2",
2163             32,
2164             [
2165                 VppRoutePath(
2166                     "0.0.0.0",
2167                     0xFFFFFFFF,
2168                     nh_table_id=2,
2169                     type=FibPathType.FIB_PATH_TYPE_SOURCE_LOOKUP,
2170                 )
2171             ],
2172         )
2173         route_to_dst.add_vpp_config()
2174         route_to_src.add_vpp_config()
2175
2176         #
2177         # packets to these destination are dropped, since they'll
2178         # hit the respective default routes in the second table
2179         #
2180         p_dst = (
2181             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2182             / IP(src="5.5.5.5", dst="1.1.1.1")
2183             / TCP(sport=1234, dport=1234)
2184             / Raw(b"\xa5" * 100)
2185         )
2186         p_src = (
2187             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2188             / IP(src="2.2.2.2", dst="1.1.1.2")
2189             / TCP(sport=1234, dport=1234)
2190             / Raw(b"\xa5" * 100)
2191         )
2192         pkts_dst = p_dst * 257
2193         pkts_src = p_src * 257
2194
2195         self.send_and_assert_no_replies(self.pg0, pkts_dst, "IP in dst table")
2196         self.send_and_assert_no_replies(self.pg0, pkts_src, "IP in src table")
2197
2198         #
2199         # add a route in the dst table to forward via pg1
2200         #
2201         route_in_dst = VppIpRoute(
2202             self,
2203             "1.1.1.1",
2204             32,
2205             [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)],
2206             table_id=1,
2207         )
2208         route_in_dst.add_vpp_config()
2209
2210         self.send_and_expect(self.pg0, pkts_dst, self.pg1)
2211
2212         #
2213         # add a route in the src table to forward via pg2
2214         #
2215         route_in_src = VppIpRoute(
2216             self,
2217             "2.2.2.2",
2218             32,
2219             [VppRoutePath(self.pg2.remote_ip4, self.pg2.sw_if_index)],
2220             table_id=2,
2221         )
2222         route_in_src.add_vpp_config()
2223         self.send_and_expect(self.pg0, pkts_src, self.pg2)
2224
2225         #
2226         # loop in the lookup DP
2227         #
2228         route_loop = VppIpRoute(
2229             self, "2.2.2.3", 32, [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=0)]
2230         )
2231         route_loop.add_vpp_config()
2232
2233         p_l = (
2234             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2235             / IP(src="2.2.2.4", dst="2.2.2.3")
2236             / TCP(sport=1234, dport=1234)
2237             / Raw(b"\xa5" * 100)
2238         )
2239
2240         self.send_and_assert_no_replies(self.pg0, p_l * 257, "IP lookup loop")
2241
2242
2243 class TestIPInput(VppTestCase):
2244     """IPv4 Input Exceptions"""
2245
2246     @classmethod
2247     def setUpClass(cls):
2248         super(TestIPInput, cls).setUpClass()
2249
2250     @classmethod
2251     def tearDownClass(cls):
2252         super(TestIPInput, cls).tearDownClass()
2253
2254     def setUp(self):
2255         super(TestIPInput, self).setUp()
2256
2257         self.create_pg_interfaces(range(2))
2258
2259         for i in self.pg_interfaces:
2260             i.admin_up()
2261             i.config_ip4()
2262             i.resolve_arp()
2263
2264     def tearDown(self):
2265         super(TestIPInput, self).tearDown()
2266         for i in self.pg_interfaces:
2267             i.unconfig_ip4()
2268             i.admin_down()
2269
2270     def test_ip_input(self):
2271         """IP Input Exceptions"""
2272
2273         # i can't find a way in scapy to construct an IP packet
2274         # with a length less than the IP header length
2275
2276         #
2277         # Packet too short - this is forwarded
2278         #
2279         p_short = (
2280             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2281             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, len=40)
2282             / UDP(sport=1234, dport=1234)
2283             / Raw(b"\xa5" * 100)
2284         )
2285
2286         rx = self.send_and_expect(self.pg0, p_short * NUM_PKTS, self.pg1)
2287
2288         #
2289         # Packet too long - this is dropped
2290         #
2291         p_long = (
2292             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2293             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, len=400)
2294             / UDP(sport=1234, dport=1234)
2295             / Raw(b"\xa5" * 100)
2296         )
2297
2298         rx = self.send_and_assert_no_replies(self.pg0, p_long * NUM_PKTS, "too long")
2299
2300         #
2301         # bad chksum - this is dropped
2302         #
2303         p_chksum = (
2304             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2305             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, chksum=400)
2306             / UDP(sport=1234, dport=1234)
2307             / Raw(b"\xa5" * 100)
2308         )
2309
2310         rx = self.send_and_assert_no_replies(
2311             self.pg0, p_chksum * NUM_PKTS, "bad checksum"
2312         )
2313
2314         #
2315         # bad version - this is dropped
2316         #
2317         p_ver = (
2318             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2319             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, version=3)
2320             / UDP(sport=1234, dport=1234)
2321             / Raw(b"\xa5" * 100)
2322         )
2323
2324         rx = self.send_and_assert_no_replies(
2325             self.pg0, p_ver * NUM_PKTS, "funky version"
2326         )
2327
2328         #
2329         # fragment offset 1 - this is dropped
2330         #
2331         p_frag = (
2332             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2333             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, frag=1)
2334             / UDP(sport=1234, dport=1234)
2335             / Raw(b"\xa5" * 100)
2336         )
2337
2338         rx = self.send_and_assert_no_replies(self.pg0, p_frag * NUM_PKTS, "frag offset")
2339
2340         #
2341         # TTL expired packet
2342         #
2343         p_ttl = (
2344             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2345             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=1)
2346             / UDP(sport=1234, dport=1234)
2347             / Raw(b"\xa5" * 100)
2348         )
2349
2350         rxs = self.send_and_expect_some(self.pg0, p_ttl * NUM_PKTS, self.pg0)
2351
2352         for rx in rxs:
2353             icmp = rx[ICMP]
2354             self.assertEqual(icmptypes[icmp.type], "time-exceeded")
2355             self.assertEqual(icmpcodes[icmp.type][icmp.code], "ttl-zero-during-transit")
2356             self.assertEqual(icmp.src, self.pg0.remote_ip4)
2357             self.assertEqual(icmp.dst, self.pg1.remote_ip4)
2358
2359         #
2360         # MTU exceeded
2361         #
2362         p_mtu = (
2363             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2364             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=10, flags="DF")
2365             / UDP(sport=1234, dport=1234)
2366             / Raw(b"\xa5" * 2000)
2367         )
2368
2369         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1500, 0, 0, 0])
2370
2371         rxs = self.send_and_expect_some(self.pg0, p_mtu * NUM_PKTS, self.pg0)
2372
2373         for rx in rxs:
2374             icmp = rx[ICMP]
2375             self.assertEqual(icmptypes[icmp.type], "dest-unreach")
2376             self.assertEqual(icmpcodes[icmp.type][icmp.code], "fragmentation-needed")
2377             self.assertEqual(icmp.nexthopmtu, 1500)
2378             self.assertEqual(icmp.src, self.pg0.remote_ip4)
2379             self.assertEqual(icmp.dst, self.pg1.remote_ip4)
2380
2381         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [2500, 0, 0, 0])
2382         rx = self.send_and_expect(self.pg0, p_mtu * NUM_PKTS, self.pg1)
2383
2384         # Reset MTU for subsequent tests
2385         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [9000, 0, 0, 0])
2386
2387         #
2388         # source address 0.0.0.0 and 25.255.255.255 and for-us
2389         #
2390         p_s0 = (
2391             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2392             / IP(src="0.0.0.0", dst=self.pg0.local_ip4)
2393             / ICMP(id=4, seq=4)
2394             / Raw(load=b"\x0a" * 18)
2395         )
2396         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
2397
2398         p_s0 = (
2399             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2400             / IP(src="255.255.255.255", dst=self.pg0.local_ip4)
2401             / ICMP(id=4, seq=4)
2402             / Raw(load=b"\x0a" * 18)
2403         )
2404         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
2405
2406
2407 class TestIPDirectedBroadcast(VppTestCase):
2408     """IPv4 Directed Broadcast"""
2409
2410     @classmethod
2411     def setUpClass(cls):
2412         super(TestIPDirectedBroadcast, cls).setUpClass()
2413
2414     @classmethod
2415     def tearDownClass(cls):
2416         super(TestIPDirectedBroadcast, cls).tearDownClass()
2417
2418     def setUp(self):
2419         super(TestIPDirectedBroadcast, self).setUp()
2420
2421         self.create_pg_interfaces(range(2))
2422
2423         for i in self.pg_interfaces:
2424             i.admin_up()
2425
2426     def tearDown(self):
2427         super(TestIPDirectedBroadcast, self).tearDown()
2428         for i in self.pg_interfaces:
2429             i.admin_down()
2430
2431     def test_ip_input(self):
2432         """IP Directed Broadcast"""
2433
2434         #
2435         # set the directed broadcast on pg0 first, then config IP4 addresses
2436         # for pg1 directed broadcast is always disabled
2437         self.vapi.sw_interface_set_ip_directed_broadcast(self.pg0.sw_if_index, 1)
2438
2439         p0 = (
2440             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
2441             / IP(src="1.1.1.1", dst=self.pg0._local_ip4_bcast)
2442             / UDP(sport=1234, dport=1234)
2443             / Raw(b"\xa5" * 2000)
2444         )
2445         p1 = (
2446             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2447             / IP(src="1.1.1.1", dst=self.pg1._local_ip4_bcast)
2448             / UDP(sport=1234, dport=1234)
2449             / Raw(b"\xa5" * 2000)
2450         )
2451
2452         self.pg0.config_ip4()
2453         self.pg0.resolve_arp()
2454         self.pg1.config_ip4()
2455         self.pg1.resolve_arp()
2456
2457         #
2458         # test packet is L2 broadcast
2459         #
2460         rx = self.send_and_expect(self.pg1, p0 * NUM_PKTS, self.pg0)
2461         self.assertTrue(rx[0][Ether].dst, "ff:ff:ff:ff:ff:ff")
2462
2463         self.send_and_assert_no_replies(
2464             self.pg0, p1 * NUM_PKTS, "directed broadcast disabled"
2465         )
2466
2467         #
2468         # toggle directed broadcast on pg0
2469         #
2470         self.vapi.sw_interface_set_ip_directed_broadcast(self.pg0.sw_if_index, 0)
2471         self.send_and_assert_no_replies(
2472             self.pg1, p0 * NUM_PKTS, "directed broadcast disabled"
2473         )
2474
2475         self.vapi.sw_interface_set_ip_directed_broadcast(self.pg0.sw_if_index, 1)
2476         rx = self.send_and_expect(self.pg1, p0 * NUM_PKTS, self.pg0)
2477
2478         self.pg0.unconfig_ip4()
2479         self.pg1.unconfig_ip4()
2480
2481
2482 class TestIPLPM(VppTestCase):
2483     """IPv4 longest Prefix Match"""
2484
2485     @classmethod
2486     def setUpClass(cls):
2487         super(TestIPLPM, cls).setUpClass()
2488
2489     @classmethod
2490     def tearDownClass(cls):
2491         super(TestIPLPM, cls).tearDownClass()
2492
2493     def setUp(self):
2494         super(TestIPLPM, self).setUp()
2495
2496         self.create_pg_interfaces(range(4))
2497
2498         for i in self.pg_interfaces:
2499             i.admin_up()
2500             i.config_ip4()
2501             i.resolve_arp()
2502
2503     def tearDown(self):
2504         super(TestIPLPM, self).tearDown()
2505         for i in self.pg_interfaces:
2506             i.admin_down()
2507             i.unconfig_ip4()
2508
2509     def test_ip_lpm(self):
2510         """IP longest Prefix Match"""
2511
2512         s_24 = VppIpRoute(
2513             self,
2514             "10.1.2.0",
2515             24,
2516             [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)],
2517         )
2518         s_24.add_vpp_config()
2519         s_8 = VppIpRoute(
2520             self,
2521             "10.0.0.0",
2522             8,
2523             [VppRoutePath(self.pg2.remote_ip4, self.pg2.sw_if_index)],
2524         )
2525         s_8.add_vpp_config()
2526
2527         p_8 = (
2528             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2529             / IP(src="1.1.1.1", dst="10.1.1.1")
2530             / UDP(sport=1234, dport=1234)
2531             / Raw(b"\xa5" * 2000)
2532         )
2533         p_24 = (
2534             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2535             / IP(src="1.1.1.1", dst="10.1.2.1")
2536             / UDP(sport=1234, dport=1234)
2537             / Raw(b"\xa5" * 2000)
2538         )
2539
2540         self.logger.info(self.vapi.cli("sh ip fib mtrie"))
2541         rx = self.send_and_expect(self.pg0, p_8 * NUM_PKTS, self.pg2)
2542         rx = self.send_and_expect(self.pg0, p_24 * NUM_PKTS, self.pg1)
2543
2544
2545 @tag_fixme_vpp_workers
2546 class TestIPv4Frag(VppTestCase):
2547     """IPv4 fragmentation"""
2548
2549     @classmethod
2550     def setUpClass(cls):
2551         super(TestIPv4Frag, cls).setUpClass()
2552
2553         cls.create_pg_interfaces([0, 1])
2554         cls.src_if = cls.pg0
2555         cls.dst_if = cls.pg1
2556
2557         # setup all interfaces
2558         for i in cls.pg_interfaces:
2559             i.admin_up()
2560             i.config_ip4()
2561             i.resolve_arp()
2562
2563     @classmethod
2564     def tearDownClass(cls):
2565         super(TestIPv4Frag, cls).tearDownClass()
2566
2567     def test_frag_large_packets(self):
2568         """Fragmentation of large packets"""
2569
2570         self.vapi.cli("adjacency counters enable")
2571
2572         p = (
2573             Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
2574             / IP(src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4)
2575             / UDP(sport=1234, dport=5678)
2576             / Raw()
2577         )
2578         self.extend_packet(p, 6000, "abcde")
2579         saved_payload = p[Raw].load
2580
2581         nbr = VppNeighbor(
2582             self,
2583             self.dst_if.sw_if_index,
2584             self.dst_if.remote_mac,
2585             self.dst_if.remote_ip4,
2586         ).add_vpp_config()
2587
2588         # Force fragmentation by setting MTU of output interface
2589         # lower than packet size
2590         self.vapi.sw_interface_set_mtu(self.dst_if.sw_if_index, [5000, 0, 0, 0])
2591
2592         self.pg_enable_capture()
2593         self.src_if.add_stream(p)
2594         self.pg_start()
2595
2596         # Expecting 3 fragments because size of created fragments currently
2597         # cannot be larger then VPP buffer size (which is 2048)
2598         packets = self.dst_if.get_capture(3)
2599
2600         # we should show 3 packets thru the neighbor
2601         self.assertEqual(3, nbr.get_stats()["packets"])
2602
2603         # Assume VPP sends the fragments in order
2604         payload = b""
2605         for p in packets:
2606             payload_offset = p.frag * 8
2607             if payload_offset > 0:
2608                 payload_offset -= 8  # UDP header is not in payload
2609             self.assert_equal(payload_offset, len(payload))
2610             payload += p[Raw].load
2611         self.assert_equal(payload, saved_payload, "payload")
2612
2613
2614 class TestIPReplace(VppTestCase):
2615     """IPv4 Table Replace"""
2616
2617     @classmethod
2618     def setUpClass(cls):
2619         super(TestIPReplace, cls).setUpClass()
2620
2621     @classmethod
2622     def tearDownClass(cls):
2623         super(TestIPReplace, cls).tearDownClass()
2624
2625     def setUp(self):
2626         super(TestIPReplace, self).setUp()
2627
2628         self.create_pg_interfaces(range(4))
2629
2630         table_id = 1
2631         self.tables = []
2632
2633         for i in self.pg_interfaces:
2634             i.admin_up()
2635             i.config_ip4()
2636             i.resolve_arp()
2637             i.generate_remote_hosts(2)
2638             self.tables.append(VppIpTable(self, table_id).add_vpp_config())
2639             table_id += 1
2640
2641     def tearDown(self):
2642         super(TestIPReplace, self).tearDown()
2643         for i in self.pg_interfaces:
2644             i.admin_down()
2645             i.unconfig_ip4()
2646
2647     def test_replace(self):
2648         """IP Table Replace"""
2649
2650         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
2651         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
2652         N_ROUTES = 20
2653         links = [self.pg0, self.pg1, self.pg2, self.pg3]
2654         routes = [[], [], [], []]
2655
2656         # load up the tables with some routes
2657         for ii, t in enumerate(self.tables):
2658             for jj in range(N_ROUTES):
2659                 uni = VppIpRoute(
2660                     self,
2661                     "10.0.0.%d" % jj,
2662                     32,
2663                     [
2664                         VppRoutePath(
2665                             links[ii].remote_hosts[0].ip4, links[ii].sw_if_index
2666                         ),
2667                         VppRoutePath(
2668                             links[ii].remote_hosts[1].ip4, links[ii].sw_if_index
2669                         ),
2670                     ],
2671                     table_id=t.table_id,
2672                 ).add_vpp_config()
2673                 multi = VppIpMRoute(
2674                     self,
2675                     "0.0.0.0",
2676                     "239.0.0.%d" % jj,
2677                     32,
2678                     MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
2679                     [
2680                         VppMRoutePath(
2681                             self.pg0.sw_if_index,
2682                             MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT,
2683                         ),
2684                         VppMRoutePath(
2685                             self.pg1.sw_if_index,
2686                             MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
2687                         ),
2688                         VppMRoutePath(
2689                             self.pg2.sw_if_index,
2690                             MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
2691                         ),
2692                         VppMRoutePath(
2693                             self.pg3.sw_if_index,
2694                             MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
2695                         ),
2696                     ],
2697                     table_id=t.table_id,
2698                 ).add_vpp_config()
2699                 routes[ii].append({"uni": uni, "multi": multi})
2700
2701         #
2702         # replace the tables a few times
2703         #
2704         for kk in range(3):
2705             # replace_begin each table
2706             for t in self.tables:
2707                 t.replace_begin()
2708
2709             # all the routes are still there
2710             for ii, t in enumerate(self.tables):
2711                 dump = t.dump()
2712                 mdump = t.mdump()
2713                 for r in routes[ii]:
2714                     self.assertTrue(find_route_in_dump(dump, r["uni"], t))
2715                     self.assertTrue(find_mroute_in_dump(mdump, r["multi"], t))
2716
2717             # redownload the even numbered routes
2718             for ii, t in enumerate(self.tables):
2719                 for jj in range(0, N_ROUTES, 2):
2720                     routes[ii][jj]["uni"].add_vpp_config()
2721                     routes[ii][jj]["multi"].add_vpp_config()
2722
2723             # signal each table replace_end
2724             for t in self.tables:
2725                 t.replace_end()
2726
2727             # we should find the even routes, but not the odd
2728             for ii, t in enumerate(self.tables):
2729                 dump = t.dump()
2730                 mdump = t.mdump()
2731                 for jj in range(0, N_ROUTES, 2):
2732                     self.assertTrue(find_route_in_dump(dump, routes[ii][jj]["uni"], t))
2733                     self.assertTrue(
2734                         find_mroute_in_dump(mdump, routes[ii][jj]["multi"], t)
2735                     )
2736                 for jj in range(1, N_ROUTES - 1, 2):
2737                     self.assertFalse(find_route_in_dump(dump, routes[ii][jj]["uni"], t))
2738                     self.assertFalse(
2739                         find_mroute_in_dump(mdump, routes[ii][jj]["multi"], t)
2740                     )
2741
2742             # reload all the routes
2743             for ii, t in enumerate(self.tables):
2744                 for r in routes[ii]:
2745                     r["uni"].add_vpp_config()
2746                     r["multi"].add_vpp_config()
2747
2748             # all the routes are still there
2749             for ii, t in enumerate(self.tables):
2750                 dump = t.dump()
2751                 mdump = t.mdump()
2752                 for r in routes[ii]:
2753                     self.assertTrue(find_route_in_dump(dump, r["uni"], t))
2754                     self.assertTrue(find_mroute_in_dump(mdump, r["multi"], t))
2755
2756         #
2757         # finally flush the tables for good measure
2758         #
2759         for t in self.tables:
2760             t.flush()
2761             self.assertEqual(len(t.dump()), 5)
2762             self.assertEqual(len(t.mdump()), 3)
2763
2764
2765 class TestIPCover(VppTestCase):
2766     """IPv4 Table Cover"""
2767
2768     @classmethod
2769     def setUpClass(cls):
2770         super(TestIPCover, cls).setUpClass()
2771
2772     @classmethod
2773     def tearDownClass(cls):
2774         super(TestIPCover, cls).tearDownClass()
2775
2776     def setUp(self):
2777         super(TestIPCover, self).setUp()
2778
2779         self.create_pg_interfaces(range(4))
2780
2781         table_id = 1
2782         self.tables = []
2783
2784         for i in self.pg_interfaces:
2785             i.admin_up()
2786             i.config_ip4()
2787             i.resolve_arp()
2788             i.generate_remote_hosts(2)
2789             self.tables.append(VppIpTable(self, table_id).add_vpp_config())
2790             table_id += 1
2791
2792     def tearDown(self):
2793         super(TestIPCover, self).tearDown()
2794         for i in self.pg_interfaces:
2795             i.admin_down()
2796             i.unconfig_ip4()
2797
2798     def test_cover(self):
2799         """IP Table Cover"""
2800
2801         # add a loop back with a /32 prefix
2802         lo = VppLoInterface(self)
2803         lo.admin_up()
2804         a = VppIpInterfaceAddress(self, lo, "127.0.0.1", 32).add_vpp_config()
2805
2806         # add a neighbour that matches the loopback's /32
2807         nbr = VppNeighbor(
2808             self, lo.sw_if_index, lo.remote_mac, "127.0.0.1"
2809         ).add_vpp_config()
2810
2811         # add the default route which will be the cover for /32
2812         r = VppIpRoute(
2813             self,
2814             "0.0.0.0",
2815             0,
2816             [VppRoutePath("127.0.0.1", lo.sw_if_index)],
2817             register=False,
2818         ).add_vpp_config()
2819
2820         # add/remove/add a longer mask cover
2821         r8 = VppIpRoute(
2822             self, "127.0.0.0", 8, [VppRoutePath("127.0.0.1", lo.sw_if_index)]
2823         ).add_vpp_config()
2824         r8.remove_vpp_config()
2825         r8.add_vpp_config()
2826         r8.remove_vpp_config()
2827
2828         # remove the default route
2829         r.remove_vpp_config()
2830
2831         # remove the interface prefix
2832         a.remove_vpp_config()
2833
2834
2835 class TestIP4Replace(VppTestCase):
2836     """IPv4 Interface Address Replace"""
2837
2838     @classmethod
2839     def setUpClass(cls):
2840         super(TestIP4Replace, cls).setUpClass()
2841
2842     @classmethod
2843     def tearDownClass(cls):
2844         super(TestIP4Replace, cls).tearDownClass()
2845
2846     def setUp(self):
2847         super(TestIP4Replace, self).setUp()
2848
2849         self.create_pg_interfaces(range(4))
2850
2851         for i in self.pg_interfaces:
2852             i.admin_up()
2853
2854     def tearDown(self):
2855         super(TestIP4Replace, self).tearDown()
2856         for i in self.pg_interfaces:
2857             i.admin_down()
2858
2859     def get_n_pfxs(self, intf):
2860         return len(self.vapi.ip_address_dump(intf.sw_if_index))
2861
2862     def test_replace(self):
2863         """IP interface address replace"""
2864
2865         intf_pfxs = [[], [], [], []]
2866
2867         # add prefixes to each of the interfaces
2868         for i in range(len(self.pg_interfaces)):
2869             intf = self.pg_interfaces[i]
2870
2871             # 172.16.x.1/24
2872             addr = "172.16.%d.1" % intf.sw_if_index
2873             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2874             intf_pfxs[i].append(a)
2875
2876             # 172.16.x.2/24 - a different address in the same subnet as above
2877             addr = "172.16.%d.2" % intf.sw_if_index
2878             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2879             intf_pfxs[i].append(a)
2880
2881             # 172.15.x.2/24 - a different address and subnet
2882             addr = "172.15.%d.2" % intf.sw_if_index
2883             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2884             intf_pfxs[i].append(a)
2885
2886         # a dump should n_address in it
2887         for intf in self.pg_interfaces:
2888             self.assertEqual(self.get_n_pfxs(intf), 3)
2889
2890         #
2891         # remove all the address thru a replace
2892         #
2893         self.vapi.sw_interface_address_replace_begin()
2894         self.vapi.sw_interface_address_replace_end()
2895         for intf in self.pg_interfaces:
2896             self.assertEqual(self.get_n_pfxs(intf), 0)
2897
2898         #
2899         # add all the interface addresses back
2900         #
2901         for p in intf_pfxs:
2902             for v in p:
2903                 v.add_vpp_config()
2904         for intf in self.pg_interfaces:
2905             self.assertEqual(self.get_n_pfxs(intf), 3)
2906
2907         #
2908         # replace again, but this time update/re-add the address on the first
2909         # two interfaces
2910         #
2911         self.vapi.sw_interface_address_replace_begin()
2912
2913         for p in intf_pfxs[:2]:
2914             for v in p:
2915                 v.add_vpp_config()
2916
2917         self.vapi.sw_interface_address_replace_end()
2918
2919         # on the first two the address still exist,
2920         # on the other two they do not
2921         for intf in self.pg_interfaces[:2]:
2922             self.assertEqual(self.get_n_pfxs(intf), 3)
2923         for p in intf_pfxs[:2]:
2924             for v in p:
2925                 self.assertTrue(v.query_vpp_config())
2926         for intf in self.pg_interfaces[2:]:
2927             self.assertEqual(self.get_n_pfxs(intf), 0)
2928
2929         #
2930         # add all the interface addresses back on the last two
2931         #
2932         for p in intf_pfxs[2:]:
2933             for v in p:
2934                 v.add_vpp_config()
2935         for intf in self.pg_interfaces:
2936             self.assertEqual(self.get_n_pfxs(intf), 3)
2937
2938         #
2939         # replace again, this time add different prefixes on all the interfaces
2940         #
2941         self.vapi.sw_interface_address_replace_begin()
2942
2943         pfxs = []
2944         for intf in self.pg_interfaces:
2945             # 172.18.x.1/24
2946             addr = "172.18.%d.1" % intf.sw_if_index
2947             pfxs.append(VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config())
2948
2949         self.vapi.sw_interface_address_replace_end()
2950
2951         # only .18 should exist on each interface
2952         for intf in self.pg_interfaces:
2953             self.assertEqual(self.get_n_pfxs(intf), 1)
2954         for pfx in pfxs:
2955             self.assertTrue(pfx.query_vpp_config())
2956
2957         #
2958         # remove everything
2959         #
2960         self.vapi.sw_interface_address_replace_begin()
2961         self.vapi.sw_interface_address_replace_end()
2962         for intf in self.pg_interfaces:
2963             self.assertEqual(self.get_n_pfxs(intf), 0)
2964
2965         #
2966         # add prefixes to each interface. post-begin add the prefix from
2967         # interface X onto interface Y. this would normally be an error
2968         # since it would generate a 'duplicate address' warning. but in
2969         # this case, since what is newly downloaded is sane, it's ok
2970         #
2971         for intf in self.pg_interfaces:
2972             # 172.18.x.1/24
2973             addr = "172.18.%d.1" % intf.sw_if_index
2974             VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2975
2976         self.vapi.sw_interface_address_replace_begin()
2977
2978         pfxs = []
2979         for intf in self.pg_interfaces:
2980             # 172.18.x.1/24
2981             addr = "172.18.%d.1" % (intf.sw_if_index + 1)
2982             pfxs.append(VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config())
2983
2984         self.vapi.sw_interface_address_replace_end()
2985
2986         self.logger.info(self.vapi.cli("sh int addr"))
2987
2988         for intf in self.pg_interfaces:
2989             self.assertEqual(self.get_n_pfxs(intf), 1)
2990         for pfx in pfxs:
2991             self.assertTrue(pfx.query_vpp_config())
2992
2993
2994 class TestIPv4PathMTU(VppTestCase):
2995     """IPv4 Path MTU"""
2996
2997     @classmethod
2998     def setUpClass(cls):
2999         super(TestIPv4PathMTU, cls).setUpClass()
3000
3001         cls.create_pg_interfaces(range(2))
3002
3003         # setup all interfaces
3004         for i in cls.pg_interfaces:
3005             i.admin_up()
3006             i.config_ip4()
3007             i.resolve_arp()
3008
3009     @classmethod
3010     def tearDownClass(cls):
3011         super(TestIPv4PathMTU, cls).tearDownClass()
3012
3013     def test_path_mtu(self):
3014         """Path MTU"""
3015
3016         #
3017         # The goal here is not to test that fragmentation works correctly,
3018         # that's done elsewhere, the intent is to ensure that the Path MTU
3019         # settings are honoured.
3020         #
3021         self.vapi.cli("adjacency counters enable")
3022
3023         # set the interface MTU to a reasonable value
3024         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1800, 0, 0, 0])
3025
3026         self.pg1.generate_remote_hosts(4)
3027
3028         p_2k = (
3029             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
3030             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4)
3031             / UDP(sport=1234, dport=5678)
3032             / Raw(b"0xa" * 640)
3033         )
3034         p_1k = (
3035             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
3036             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4)
3037             / UDP(sport=1234, dport=5678)
3038             / Raw(b"0xa" * 320)
3039         )
3040
3041         nbr = VppNeighbor(
3042             self, self.pg1.sw_if_index, self.pg1.remote_mac, self.pg1.remote_ip4
3043         ).add_vpp_config()
3044
3045         # this is now the interface MTU frags
3046         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=2)
3047         self.send_and_expect(self.pg0, [p_1k], self.pg1)
3048
3049         # drop the path MTU for this neighbour to below the interface MTU
3050         # expect more frags
3051         pmtu = VppIpPathMtu(self, self.pg1.remote_ip4, 900).add_vpp_config()
3052
3053         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3054         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3055
3056         # print/format the adj delegate
3057         self.logger.info(self.vapi.cli("sh adj 5"))
3058
3059         # increase the path MTU to more than the interface
3060         # expect to use the interface MTU
3061         pmtu.modify(8192)
3062
3063         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=2)
3064         self.send_and_expect(self.pg0, [p_1k], self.pg1)
3065
3066         # go back to an MTU from the path
3067         # wrap the call around mark-n-sweep to enusre updates clear stale
3068         self.vapi.ip_path_mtu_replace_begin()
3069         pmtu.modify(900)
3070         self.vapi.ip_path_mtu_replace_end()
3071
3072         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3073         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3074
3075         # raise the interface's MTU
3076         # should still use that of the path
3077         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [2000, 0, 0, 0])
3078         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3079         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3080
3081         # set path high and interface low
3082         pmtu.modify(2000)
3083         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [900, 0, 0, 0])
3084         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3085         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3086
3087         # remove the path MTU using the mark-n-sweep semantics
3088         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1800, 0, 0, 0])
3089         self.vapi.ip_path_mtu_replace_begin()
3090         self.vapi.ip_path_mtu_replace_end()
3091
3092         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=2)
3093         self.send_and_expect(self.pg0, [p_1k], self.pg1)
3094
3095         #
3096         # set path MTU for a neighbour that doesn't exist, yet
3097         #
3098         pmtu2 = VppIpPathMtu(self, self.pg1.remote_hosts[2].ip4, 900).add_vpp_config()
3099
3100         p_2k = (
3101             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
3102             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_hosts[2].ip4)
3103             / UDP(sport=1234, dport=5678)
3104             / Raw(b"0xa" * 640)
3105         )
3106         p_1k = (
3107             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
3108             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_hosts[2].ip4)
3109             / UDP(sport=1234, dport=5678)
3110             / Raw(b"0xa" * 320)
3111         )
3112
3113         nbr2 = VppNeighbor(
3114             self,
3115             self.pg1.sw_if_index,
3116             self.pg1.remote_hosts[2].mac,
3117             self.pg1.remote_hosts[2].ip4,
3118         ).add_vpp_config()
3119
3120         # should frag to the path MTU
3121         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3122         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3123
3124         # remove and re-add the neighbour
3125         nbr2.remove_vpp_config()
3126         nbr2.add_vpp_config()
3127
3128         # should frag to the path MTU
3129         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3130         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3131
3132         #
3133         # set PMTUs for many peers
3134         #
3135         N_HOSTS = 16
3136         self.pg1.generate_remote_hosts(16)
3137         self.pg1.configure_ipv4_neighbors()
3138
3139         for h in range(N_HOSTS):
3140             pmtu = VppIpPathMtu(self, self.pg1.remote_hosts[h].ip4, 900)
3141             pmtu.add_vpp_config()
3142             self.assertTrue(pmtu.query_vpp_config())
3143
3144         self.logger.info(self.vapi.cli("sh ip pmtu"))
3145         dump = list(self.vapi.vpp.details_iter(self.vapi.ip_path_mtu_get))
3146         self.assertEqual(N_HOSTS, len(dump))
3147
3148         for h in range(N_HOSTS):
3149             p_2k[IP].dst = self.pg1.remote_hosts[h].ip4
3150             p_1k[IP].dst = self.pg1.remote_hosts[h].ip4
3151
3152             # should frag to the path MTU
3153             self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3154             self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3155
3156
3157 class TestIPv4ItfRebind(VppTestCase):
3158     """IPv4 Interface Bind w/ attached routes"""
3159
3160     def setUp(self):
3161         super(TestIPv4ItfRebind, self).setUp()
3162
3163         self.create_pg_interfaces(range(3))
3164
3165     def tearDown(self):
3166         super(TestIPv4ItfRebind, self).tearDown()
3167
3168     def test_rebind(self):
3169         """Import to no import"""
3170
3171         TABLE_ID = 1
3172         tbl = VppIpTable(self, TABLE_ID).add_vpp_config()
3173         self.pg1.set_table_ip4(TABLE_ID)
3174
3175         for i in self.pg_interfaces:
3176             i.admin_up()
3177             i.config_ip4()
3178             i.resolve_arp()
3179
3180         # add an attached route via an pg0
3181         # in a different table. this prefix should import
3182         rt = VppIpRoute(
3183             self,
3184             self.pg0.local_ip4,
3185             24,
3186             [VppRoutePath("0.0.0.0", self.pg0.sw_if_index)],
3187             table_id=TABLE_ID,
3188         ).add_vpp_config()
3189
3190         p = (
3191             Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
3192             / IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4)
3193             / UDP(sport=1234, dport=5678)
3194             / Raw(b"0xa" * 640)
3195         )
3196
3197         rx = self.send_and_expect(self.pg1, [p], self.pg0)
3198         self.assertFalse(rx[0].haslayer(ARP))
3199
3200         # then bind pg0 to a new table
3201         # so the prefix no longer imports
3202         self.pg0.unconfig_ip4()
3203         self.pg0.set_table_ip4(TABLE_ID)
3204         self.pg0.config_ip4()
3205         self.pg0.resolve_arp()
3206
3207         rx = self.send_and_expect(self.pg1, [p], self.pg0)
3208         self.assertFalse(rx[0].haslayer(ARP))
3209
3210         # revert back to imported
3211         self.pg0.unconfig_ip4()
3212         self.pg0.set_table_ip4(0)
3213         self.pg0.config_ip4()
3214         self.pg0.resolve_arp()
3215
3216         rx = self.send_and_expect(self.pg1, [p], self.pg0)
3217         self.assertFalse(rx[0].haslayer(ARP))
3218
3219         # cleanup
3220         for i in self.pg_interfaces:
3221             i.unconfig_ip4()
3222             i.set_table_ip4(0)
3223             i.admin_down()
3224
3225         rt.remove_vpp_config()
3226         tbl.remove_vpp_config()
3227
3228     def test_delete(self):
3229         """Swap import tables"""
3230
3231         TABLE_ID1 = 1
3232         tbl1_4 = VppIpTable(self, TABLE_ID1).add_vpp_config()
3233         tbl1_6 = VppIpTable(self, TABLE_ID1, True).add_vpp_config()
3234         TABLE_ID2 = 2
3235         tbl2_4 = VppIpTable(self, TABLE_ID2).add_vpp_config()
3236         tbl2_6 = VppIpTable(self, TABLE_ID2, True).add_vpp_config()
3237
3238         # table mappings
3239         self.pg1.set_table_ip4(TABLE_ID1)
3240         self.pg1.set_table_ip6(TABLE_ID1)
3241         self.pg2.set_table_ip4(TABLE_ID2)
3242         self.pg2.set_table_ip6(TABLE_ID2)
3243
3244         for i in self.pg_interfaces:
3245             i.admin_up()
3246             i.config_ip4()
3247             i.resolve_arp()
3248
3249         # add an attached route in the default table via pg0
3250         # this should import to table 1
3251         rt4 = VppIpRoute(
3252             self,
3253             self.pg1.local_ip4,
3254             24,
3255             [VppRoutePath("0.0.0.0", self.pg1.sw_if_index)],
3256         ).add_vpp_config()
3257         rt6 = VppIpRoute(
3258             self,
3259             self.pg1.local_ip6,
3260             64,
3261             [VppRoutePath("0.0.0.0", self.pg1.sw_if_index)],
3262         ).add_vpp_config()
3263
3264         p1 = (
3265             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
3266             / IP(src=self.pg1.remote_ip4, dst=self.pg1.remote_ip4)
3267             / UDP(sport=1234, dport=5678)
3268             / Raw(b"0xa" * 640)
3269         )
3270
3271         # inject into table 0
3272         rx = self.send_and_expect(self.pg0, [p1], self.pg1)
3273         self.assertFalse(rx[0].haslayer(ARP))
3274
3275         # swap the attached interface to table 2
3276         self.pg1.unconfig_ip4()
3277         self.pg1.unconfig_ip6()
3278         self.pg1.set_table_ip4(TABLE_ID2)
3279         self.pg1.set_table_ip6(TABLE_ID2)
3280         self.pg1.config_ip4()
3281         self.pg1.config_ip6()
3282         self.pg1.resolve_arp()
3283
3284         # delete table 1
3285         tbl1_4.flush()
3286         tbl1_6.flush()
3287         tbl1_4.remove_vpp_config()
3288         tbl1_6.remove_vpp_config()
3289
3290         rx = self.send_and_expect(self.pg0, [p1], self.pg1)
3291         self.assertFalse(rx[0].haslayer(ARP))
3292
3293         for i in self.pg_interfaces:
3294             i.unconfig_ip4()
3295             i.unconfig_ip6()
3296             i.set_table_ip4(0)
3297             i.set_table_ip6(0)
3298             i.admin_down()
3299
3300
3301 if __name__ == "__main__":
3302     unittest.main(testRunner=VppTestRunner)