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