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