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