tests: replace pycodestyle with black
[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(
1797             "/err/ip4-local/ip4 source lookup miss", len(pkts) + 1
1798         )
1799
1800         # using the same source in different tables, should reject
1801         # for the table that the source is not present in
1802         # the first packet in the stream is drop
1803         pkts = [
1804             (
1805                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1806                 / Dot1Q(vlan=i.vlan)
1807                 / IP(src=vlans_pg0[0].remote_ip4, dst=i.local_ip4)
1808                 / UDP(sport=1234, dport=1234)
1809                 / Raw(b"\xa5" * 100)
1810             )
1811             for i in vlans_pg0
1812         ]
1813         # single loop accept and drop
1814         # followed by both in the same frame/loop
1815         self.send_and_expect(self.pg0, [pkts[0]], self.pg1)
1816         self.send_and_assert_no_replies(self.pg0, [pkts[1]])
1817         self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
1818
1819         # using the same source in different tables, should reject
1820         # for the table that the source is not present in
1821         # the first packet in the stream is accept
1822         pkts = [
1823             (
1824                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1825                 / Dot1Q(vlan=i.vlan)
1826                 / IP(src=vlans_pg0[3].remote_ip4, dst=i.local_ip4)
1827                 / UDP(sport=1234, dport=1234)
1828                 / Raw(b"\xa5" * 100)
1829             )
1830             for i in vlans_pg0
1831         ]
1832
1833         # single loop accept and drop
1834         # followed by both in the same frame/loop
1835         self.send_and_expect(self.pg0, [pkts[3]], self.pg1)
1836         self.send_and_assert_no_replies(self.pg0, [pkts[1]])
1837         self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
1838
1839         #
1840         # IPv6
1841         #
1842
1843         # we reject packets for source addresses in the wrong vlan/VRF
1844         pkts = [
1845             (
1846                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1847                 / Dot1Q(vlan=i.vlan)
1848                 / IPv6(src="1::1", dst=i.local_ip6)
1849                 / UDP(sport=1236, dport=1236)
1850                 / Raw(b"\xa5" * 100)
1851             )
1852             for i in vlans_pg0
1853         ]
1854         # single and dual loop
1855         self.send_and_assert_no_replies(self.pg0, [pkts[0]])
1856         self.send_and_assert_no_replies(self.pg0, pkts)
1857
1858         self.assert_error_counter_equal(
1859             "/err/ip6-input/ip6 source lookup miss", len(pkts) + 1
1860         )
1861
1862         # using the same source in different tables, should reject
1863         # for the table that the source is not present in
1864         # the first packet in the stream is drop
1865         pkts = [
1866             (
1867                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1868                 / Dot1Q(vlan=i.vlan)
1869                 / IPv6(src=vlans_pg0[0].remote_ip6, dst=i.local_ip6)
1870                 / UDP(sport=1236, dport=1236)
1871                 / Raw(b"\xa5" * 100)
1872             )
1873             for i in vlans_pg0
1874         ]
1875         # single loop accept and drop
1876         # followed by both in the same frame/loop
1877         self.send_and_expect(self.pg0, [pkts[0]], self.pg1)
1878         self.send_and_assert_no_replies(self.pg0, [pkts[1]])
1879         self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
1880
1881         # using the same source in different tables, should reject
1882         # for the table that the source is not present in
1883         # the first packet in the stream is accept
1884         pkts = [
1885             (
1886                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
1887                 / Dot1Q(vlan=i.vlan)
1888                 / IPv6(src=vlans_pg0[3].remote_ip6, dst=i.local_ip6)
1889                 / UDP(sport=1236, dport=1236)
1890                 / Raw(b"\xa5" * 100)
1891             )
1892             for i in vlans_pg0
1893         ]
1894
1895         # single loop accept and drop
1896         # followed by both in the same frame/loop
1897         self.send_and_expect(self.pg0, [pkts[3]], self.pg1)
1898         self.send_and_assert_no_replies(self.pg0, [pkts[1]])
1899         self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
1900
1901         for v in vlans_pg0 + vlans_pg1:
1902             v.unconfig_ip4()
1903             v.unconfig_ip6()
1904             v.set_table_ip4(0)
1905             v.set_table_ip6(0)
1906
1907     def test_ip_punt_dump(self):
1908         """IP4 punt redirect dump"""
1909
1910         #
1911         # Configure a punt redirects
1912         #
1913         nh_address = self.pg3.remote_ip4
1914         ipr_03 = VppIpPuntRedirect(
1915             self, self.pg0.sw_if_index, self.pg3.sw_if_index, nh_address
1916         )
1917         ipr_13 = VppIpPuntRedirect(
1918             self, self.pg1.sw_if_index, self.pg3.sw_if_index, nh_address
1919         )
1920         ipr_23 = VppIpPuntRedirect(
1921             self, self.pg2.sw_if_index, self.pg3.sw_if_index, "0.0.0.0"
1922         )
1923         ipr_03.add_vpp_config()
1924         ipr_13.add_vpp_config()
1925         ipr_23.add_vpp_config()
1926
1927         #
1928         # Dump pg0 punt redirects
1929         #
1930         self.assertTrue(ipr_03.query_vpp_config())
1931         self.assertTrue(ipr_13.query_vpp_config())
1932         self.assertTrue(ipr_23.query_vpp_config())
1933
1934         #
1935         # Dump punt redirects for all interfaces
1936         #
1937         punts = self.vapi.ip_punt_redirect_dump(0xFFFFFFFF)
1938         self.assertEqual(len(punts), 3)
1939         for p in punts:
1940             self.assertEqual(p.punt.tx_sw_if_index, self.pg3.sw_if_index)
1941         self.assertNotEqual(punts[1].punt.nh, self.pg3.remote_ip4)
1942         self.assertEqual(str(punts[2].punt.nh), "0.0.0.0")
1943
1944
1945 class TestIPPuntHandoff(IPPuntSetup, VppTestCase):
1946     """IPv4 Punt Policer thread handoff"""
1947
1948     vpp_worker_count = 2
1949
1950     def setUp(self):
1951         super(TestIPPuntHandoff, self).setUp()
1952         super(TestIPPuntHandoff, self).punt_setup()
1953
1954     def tearDown(self):
1955         super(TestIPPuntHandoff, self).punt_teardown()
1956         super(TestIPPuntHandoff, self).tearDown()
1957
1958     def test_ip_punt_policer_handoff(self):
1959         """IP4 punt policer thread handoff"""
1960         pkts = self.pkt * NUM_PKTS
1961
1962         #
1963         # Configure a punt redirect via pg1.
1964         #
1965         nh_addr = self.pg1.remote_ip4
1966         ip_punt_redirect = VppIpPuntRedirect(
1967             self, self.pg0.sw_if_index, self.pg1.sw_if_index, nh_addr
1968         )
1969         ip_punt_redirect.add_vpp_config()
1970
1971         action_tx = PolicerAction(
1972             VppEnum.vl_api_sse2_qos_action_type_t.SSE2_QOS_ACTION_API_TRANSMIT, 0
1973         )
1974         #
1975         # This policer drops no packets, we are just
1976         # testing that they get to the right thread.
1977         #
1978         policer = VppPolicer(
1979             self,
1980             "ip4-punt",
1981             400,
1982             0,
1983             10,
1984             0,
1985             1,
1986             0,
1987             0,
1988             False,
1989             action_tx,
1990             action_tx,
1991             action_tx,
1992         )
1993         policer.add_vpp_config()
1994         ip_punt_policer = VppIpPuntPolicer(self, policer.policer_index)
1995         ip_punt_policer.add_vpp_config()
1996
1997         for worker in [0, 1]:
1998             self.send_and_expect(self.pg0, pkts, self.pg1, worker=worker)
1999             self.logger.debug(self.vapi.cli("show trace max 100"))
2000
2001         # Combined stats, all threads
2002         stats = policer.get_stats()
2003
2004         # Single rate policer - expect conform, violate but no exceed
2005         self.assertGreater(stats["conform_packets"], 0)
2006         self.assertEqual(stats["exceed_packets"], 0)
2007         self.assertGreater(stats["violate_packets"], 0)
2008
2009         # Worker 0, should have done all the policing
2010         stats0 = policer.get_stats(worker=0)
2011         self.assertEqual(stats, stats0)
2012
2013         # Worker 1, should have handed everything off
2014         stats1 = policer.get_stats(worker=1)
2015         self.assertEqual(stats1["conform_packets"], 0)
2016         self.assertEqual(stats1["exceed_packets"], 0)
2017         self.assertEqual(stats1["violate_packets"], 0)
2018
2019         # Bind the policer to worker 1 and repeat
2020         policer.bind_vpp_config(1, True)
2021         for worker in [0, 1]:
2022             self.send_and_expect(self.pg0, pkts, self.pg1, worker=worker)
2023             self.logger.debug(self.vapi.cli("show trace max 100"))
2024
2025         # The 2 workers should now have policed the same amount
2026         stats = policer.get_stats()
2027         stats0 = policer.get_stats(worker=0)
2028         stats1 = policer.get_stats(worker=1)
2029
2030         self.assertGreater(stats0["conform_packets"], 0)
2031         self.assertEqual(stats0["exceed_packets"], 0)
2032         self.assertGreater(stats0["violate_packets"], 0)
2033
2034         self.assertGreater(stats1["conform_packets"], 0)
2035         self.assertEqual(stats1["exceed_packets"], 0)
2036         self.assertGreater(stats1["violate_packets"], 0)
2037
2038         self.assertEqual(
2039             stats0["conform_packets"] + stats1["conform_packets"],
2040             stats["conform_packets"],
2041         )
2042
2043         self.assertEqual(
2044             stats0["violate_packets"] + stats1["violate_packets"],
2045             stats["violate_packets"],
2046         )
2047
2048         # Unbind the policer and repeat
2049         policer.bind_vpp_config(1, False)
2050         for worker in [0, 1]:
2051             self.send_and_expect(self.pg0, pkts, self.pg1, worker=worker)
2052             self.logger.debug(self.vapi.cli("show trace max 100"))
2053
2054         # The policer should auto-bind to worker 0 when packets arrive
2055         stats = policer.get_stats()
2056         stats0new = policer.get_stats(worker=0)
2057         stats1new = policer.get_stats(worker=1)
2058
2059         self.assertGreater(stats0new["conform_packets"], stats0["conform_packets"])
2060         self.assertEqual(stats0new["exceed_packets"], 0)
2061         self.assertGreater(stats0new["violate_packets"], stats0["violate_packets"])
2062
2063         self.assertEqual(stats1, stats1new)
2064
2065         #
2066         # Clean up
2067         #
2068         ip_punt_policer.remove_vpp_config()
2069         policer.remove_vpp_config()
2070         ip_punt_redirect.remove_vpp_config()
2071
2072
2073 class TestIPDeag(VppTestCase):
2074     """IPv4 Deaggregate Routes"""
2075
2076     @classmethod
2077     def setUpClass(cls):
2078         super(TestIPDeag, cls).setUpClass()
2079
2080     @classmethod
2081     def tearDownClass(cls):
2082         super(TestIPDeag, cls).tearDownClass()
2083
2084     def setUp(self):
2085         super(TestIPDeag, self).setUp()
2086
2087         self.create_pg_interfaces(range(3))
2088
2089         for i in self.pg_interfaces:
2090             i.admin_up()
2091             i.config_ip4()
2092             i.resolve_arp()
2093
2094     def tearDown(self):
2095         super(TestIPDeag, self).tearDown()
2096         for i in self.pg_interfaces:
2097             i.unconfig_ip4()
2098             i.admin_down()
2099
2100     def test_ip_deag(self):
2101         """IP Deag Routes"""
2102
2103         #
2104         # Create a table to be used for:
2105         #  1 - another destination address lookup
2106         #  2 - a source address lookup
2107         #
2108         table_dst = VppIpTable(self, 1)
2109         table_src = VppIpTable(self, 2)
2110         table_dst.add_vpp_config()
2111         table_src.add_vpp_config()
2112
2113         #
2114         # Add a route in the default table to point to a deag/
2115         # second lookup in each of these tables
2116         #
2117         route_to_dst = VppIpRoute(
2118             self, "1.1.1.1", 32, [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=1)]
2119         )
2120         route_to_src = VppIpRoute(
2121             self,
2122             "1.1.1.2",
2123             32,
2124             [
2125                 VppRoutePath(
2126                     "0.0.0.0",
2127                     0xFFFFFFFF,
2128                     nh_table_id=2,
2129                     type=FibPathType.FIB_PATH_TYPE_SOURCE_LOOKUP,
2130                 )
2131             ],
2132         )
2133         route_to_dst.add_vpp_config()
2134         route_to_src.add_vpp_config()
2135
2136         #
2137         # packets to these destination are dropped, since they'll
2138         # hit the respective default routes in the second table
2139         #
2140         p_dst = (
2141             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2142             / IP(src="5.5.5.5", dst="1.1.1.1")
2143             / TCP(sport=1234, dport=1234)
2144             / Raw(b"\xa5" * 100)
2145         )
2146         p_src = (
2147             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2148             / IP(src="2.2.2.2", dst="1.1.1.2")
2149             / TCP(sport=1234, dport=1234)
2150             / Raw(b"\xa5" * 100)
2151         )
2152         pkts_dst = p_dst * 257
2153         pkts_src = p_src * 257
2154
2155         self.send_and_assert_no_replies(self.pg0, pkts_dst, "IP in dst table")
2156         self.send_and_assert_no_replies(self.pg0, pkts_src, "IP in src table")
2157
2158         #
2159         # add a route in the dst table to forward via pg1
2160         #
2161         route_in_dst = VppIpRoute(
2162             self,
2163             "1.1.1.1",
2164             32,
2165             [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)],
2166             table_id=1,
2167         )
2168         route_in_dst.add_vpp_config()
2169
2170         self.send_and_expect(self.pg0, pkts_dst, self.pg1)
2171
2172         #
2173         # add a route in the src table to forward via pg2
2174         #
2175         route_in_src = VppIpRoute(
2176             self,
2177             "2.2.2.2",
2178             32,
2179             [VppRoutePath(self.pg2.remote_ip4, self.pg2.sw_if_index)],
2180             table_id=2,
2181         )
2182         route_in_src.add_vpp_config()
2183         self.send_and_expect(self.pg0, pkts_src, self.pg2)
2184
2185         #
2186         # loop in the lookup DP
2187         #
2188         route_loop = VppIpRoute(
2189             self, "2.2.2.3", 32, [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=0)]
2190         )
2191         route_loop.add_vpp_config()
2192
2193         p_l = (
2194             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2195             / IP(src="2.2.2.4", dst="2.2.2.3")
2196             / TCP(sport=1234, dport=1234)
2197             / Raw(b"\xa5" * 100)
2198         )
2199
2200         self.send_and_assert_no_replies(self.pg0, p_l * 257, "IP lookup loop")
2201
2202
2203 class TestIPInput(VppTestCase):
2204     """IPv4 Input Exceptions"""
2205
2206     @classmethod
2207     def setUpClass(cls):
2208         super(TestIPInput, cls).setUpClass()
2209
2210     @classmethod
2211     def tearDownClass(cls):
2212         super(TestIPInput, cls).tearDownClass()
2213
2214     def setUp(self):
2215         super(TestIPInput, self).setUp()
2216
2217         self.create_pg_interfaces(range(2))
2218
2219         for i in self.pg_interfaces:
2220             i.admin_up()
2221             i.config_ip4()
2222             i.resolve_arp()
2223
2224     def tearDown(self):
2225         super(TestIPInput, self).tearDown()
2226         for i in self.pg_interfaces:
2227             i.unconfig_ip4()
2228             i.admin_down()
2229
2230     def test_ip_input(self):
2231         """IP Input Exceptions"""
2232
2233         # i can't find a way in scapy to construct an IP packet
2234         # with a length less than the IP header length
2235
2236         #
2237         # Packet too short - this is forwarded
2238         #
2239         p_short = (
2240             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2241             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, len=40)
2242             / UDP(sport=1234, dport=1234)
2243             / Raw(b"\xa5" * 100)
2244         )
2245
2246         rx = self.send_and_expect(self.pg0, p_short * NUM_PKTS, self.pg1)
2247
2248         #
2249         # Packet too long - this is dropped
2250         #
2251         p_long = (
2252             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2253             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, len=400)
2254             / UDP(sport=1234, dport=1234)
2255             / Raw(b"\xa5" * 100)
2256         )
2257
2258         rx = self.send_and_assert_no_replies(self.pg0, p_long * NUM_PKTS, "too long")
2259
2260         #
2261         # bad chksum - this is dropped
2262         #
2263         p_chksum = (
2264             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2265             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, chksum=400)
2266             / UDP(sport=1234, dport=1234)
2267             / Raw(b"\xa5" * 100)
2268         )
2269
2270         rx = self.send_and_assert_no_replies(
2271             self.pg0, p_chksum * NUM_PKTS, "bad checksum"
2272         )
2273
2274         #
2275         # bad version - this is dropped
2276         #
2277         p_ver = (
2278             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2279             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, version=3)
2280             / UDP(sport=1234, dport=1234)
2281             / Raw(b"\xa5" * 100)
2282         )
2283
2284         rx = self.send_and_assert_no_replies(
2285             self.pg0, p_ver * NUM_PKTS, "funky version"
2286         )
2287
2288         #
2289         # fragment offset 1 - this is dropped
2290         #
2291         p_frag = (
2292             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2293             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, frag=1)
2294             / UDP(sport=1234, dport=1234)
2295             / Raw(b"\xa5" * 100)
2296         )
2297
2298         rx = self.send_and_assert_no_replies(self.pg0, p_frag * NUM_PKTS, "frag offset")
2299
2300         #
2301         # TTL expired packet
2302         #
2303         p_ttl = (
2304             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2305             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=1)
2306             / UDP(sport=1234, dport=1234)
2307             / Raw(b"\xa5" * 100)
2308         )
2309
2310         rxs = self.send_and_expect_some(self.pg0, p_ttl * NUM_PKTS, self.pg0)
2311
2312         for rx in rxs:
2313             icmp = rx[ICMP]
2314             self.assertEqual(icmptypes[icmp.type], "time-exceeded")
2315             self.assertEqual(icmpcodes[icmp.type][icmp.code], "ttl-zero-during-transit")
2316             self.assertEqual(icmp.src, self.pg0.remote_ip4)
2317             self.assertEqual(icmp.dst, self.pg1.remote_ip4)
2318
2319         #
2320         # MTU exceeded
2321         #
2322         p_mtu = (
2323             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2324             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=10, flags="DF")
2325             / UDP(sport=1234, dport=1234)
2326             / Raw(b"\xa5" * 2000)
2327         )
2328
2329         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1500, 0, 0, 0])
2330
2331         rxs = self.send_and_expect_some(self.pg0, p_mtu * NUM_PKTS, self.pg0)
2332
2333         for rx in rxs:
2334             icmp = rx[ICMP]
2335             self.assertEqual(icmptypes[icmp.type], "dest-unreach")
2336             self.assertEqual(icmpcodes[icmp.type][icmp.code], "fragmentation-needed")
2337             self.assertEqual(icmp.nexthopmtu, 1500)
2338             self.assertEqual(icmp.src, self.pg0.remote_ip4)
2339             self.assertEqual(icmp.dst, self.pg1.remote_ip4)
2340
2341         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [2500, 0, 0, 0])
2342         rx = self.send_and_expect(self.pg0, p_mtu * NUM_PKTS, self.pg1)
2343
2344         # Reset MTU for subsequent tests
2345         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [9000, 0, 0, 0])
2346
2347         #
2348         # source address 0.0.0.0 and 25.255.255.255 and for-us
2349         #
2350         p_s0 = (
2351             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2352             / IP(src="0.0.0.0", dst=self.pg0.local_ip4)
2353             / ICMP(id=4, seq=4)
2354             / Raw(load=b"\x0a" * 18)
2355         )
2356         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
2357
2358         p_s0 = (
2359             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2360             / IP(src="255.255.255.255", dst=self.pg0.local_ip4)
2361             / ICMP(id=4, seq=4)
2362             / Raw(load=b"\x0a" * 18)
2363         )
2364         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
2365
2366
2367 class TestIPDirectedBroadcast(VppTestCase):
2368     """IPv4 Directed Broadcast"""
2369
2370     @classmethod
2371     def setUpClass(cls):
2372         super(TestIPDirectedBroadcast, cls).setUpClass()
2373
2374     @classmethod
2375     def tearDownClass(cls):
2376         super(TestIPDirectedBroadcast, cls).tearDownClass()
2377
2378     def setUp(self):
2379         super(TestIPDirectedBroadcast, self).setUp()
2380
2381         self.create_pg_interfaces(range(2))
2382
2383         for i in self.pg_interfaces:
2384             i.admin_up()
2385
2386     def tearDown(self):
2387         super(TestIPDirectedBroadcast, self).tearDown()
2388         for i in self.pg_interfaces:
2389             i.admin_down()
2390
2391     def test_ip_input(self):
2392         """IP Directed Broadcast"""
2393
2394         #
2395         # set the directed broadcast on pg0 first, then config IP4 addresses
2396         # for pg1 directed broadcast is always disabled
2397         self.vapi.sw_interface_set_ip_directed_broadcast(self.pg0.sw_if_index, 1)
2398
2399         p0 = (
2400             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
2401             / IP(src="1.1.1.1", dst=self.pg0._local_ip4_bcast)
2402             / UDP(sport=1234, dport=1234)
2403             / Raw(b"\xa5" * 2000)
2404         )
2405         p1 = (
2406             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2407             / IP(src="1.1.1.1", dst=self.pg1._local_ip4_bcast)
2408             / UDP(sport=1234, dport=1234)
2409             / Raw(b"\xa5" * 2000)
2410         )
2411
2412         self.pg0.config_ip4()
2413         self.pg0.resolve_arp()
2414         self.pg1.config_ip4()
2415         self.pg1.resolve_arp()
2416
2417         #
2418         # test packet is L2 broadcast
2419         #
2420         rx = self.send_and_expect(self.pg1, p0 * NUM_PKTS, self.pg0)
2421         self.assertTrue(rx[0][Ether].dst, "ff:ff:ff:ff:ff:ff")
2422
2423         self.send_and_assert_no_replies(
2424             self.pg0, p1 * NUM_PKTS, "directed broadcast disabled"
2425         )
2426
2427         #
2428         # toggle directed broadcast on pg0
2429         #
2430         self.vapi.sw_interface_set_ip_directed_broadcast(self.pg0.sw_if_index, 0)
2431         self.send_and_assert_no_replies(
2432             self.pg1, p0 * NUM_PKTS, "directed broadcast disabled"
2433         )
2434
2435         self.vapi.sw_interface_set_ip_directed_broadcast(self.pg0.sw_if_index, 1)
2436         rx = self.send_and_expect(self.pg1, p0 * NUM_PKTS, self.pg0)
2437
2438         self.pg0.unconfig_ip4()
2439         self.pg1.unconfig_ip4()
2440
2441
2442 class TestIPLPM(VppTestCase):
2443     """IPv4 longest Prefix Match"""
2444
2445     @classmethod
2446     def setUpClass(cls):
2447         super(TestIPLPM, cls).setUpClass()
2448
2449     @classmethod
2450     def tearDownClass(cls):
2451         super(TestIPLPM, cls).tearDownClass()
2452
2453     def setUp(self):
2454         super(TestIPLPM, self).setUp()
2455
2456         self.create_pg_interfaces(range(4))
2457
2458         for i in self.pg_interfaces:
2459             i.admin_up()
2460             i.config_ip4()
2461             i.resolve_arp()
2462
2463     def tearDown(self):
2464         super(TestIPLPM, self).tearDown()
2465         for i in self.pg_interfaces:
2466             i.admin_down()
2467             i.unconfig_ip4()
2468
2469     def test_ip_lpm(self):
2470         """IP longest Prefix Match"""
2471
2472         s_24 = VppIpRoute(
2473             self,
2474             "10.1.2.0",
2475             24,
2476             [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)],
2477         )
2478         s_24.add_vpp_config()
2479         s_8 = VppIpRoute(
2480             self,
2481             "10.0.0.0",
2482             8,
2483             [VppRoutePath(self.pg2.remote_ip4, self.pg2.sw_if_index)],
2484         )
2485         s_8.add_vpp_config()
2486
2487         p_8 = (
2488             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2489             / IP(src="1.1.1.1", dst="10.1.1.1")
2490             / UDP(sport=1234, dport=1234)
2491             / Raw(b"\xa5" * 2000)
2492         )
2493         p_24 = (
2494             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
2495             / IP(src="1.1.1.1", dst="10.1.2.1")
2496             / UDP(sport=1234, dport=1234)
2497             / Raw(b"\xa5" * 2000)
2498         )
2499
2500         self.logger.info(self.vapi.cli("sh ip fib mtrie"))
2501         rx = self.send_and_expect(self.pg0, p_8 * NUM_PKTS, self.pg2)
2502         rx = self.send_and_expect(self.pg0, p_24 * NUM_PKTS, self.pg1)
2503
2504
2505 @tag_fixme_vpp_workers
2506 class TestIPv4Frag(VppTestCase):
2507     """IPv4 fragmentation"""
2508
2509     @classmethod
2510     def setUpClass(cls):
2511         super(TestIPv4Frag, cls).setUpClass()
2512
2513         cls.create_pg_interfaces([0, 1])
2514         cls.src_if = cls.pg0
2515         cls.dst_if = cls.pg1
2516
2517         # setup all interfaces
2518         for i in cls.pg_interfaces:
2519             i.admin_up()
2520             i.config_ip4()
2521             i.resolve_arp()
2522
2523     @classmethod
2524     def tearDownClass(cls):
2525         super(TestIPv4Frag, cls).tearDownClass()
2526
2527     def test_frag_large_packets(self):
2528         """Fragmentation of large packets"""
2529
2530         self.vapi.cli("adjacency counters enable")
2531
2532         p = (
2533             Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac)
2534             / IP(src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4)
2535             / UDP(sport=1234, dport=5678)
2536             / Raw()
2537         )
2538         self.extend_packet(p, 6000, "abcde")
2539         saved_payload = p[Raw].load
2540
2541         nbr = VppNeighbor(
2542             self,
2543             self.dst_if.sw_if_index,
2544             self.dst_if.remote_mac,
2545             self.dst_if.remote_ip4,
2546         ).add_vpp_config()
2547
2548         # Force fragmentation by setting MTU of output interface
2549         # lower than packet size
2550         self.vapi.sw_interface_set_mtu(self.dst_if.sw_if_index, [5000, 0, 0, 0])
2551
2552         self.pg_enable_capture()
2553         self.src_if.add_stream(p)
2554         self.pg_start()
2555
2556         # Expecting 3 fragments because size of created fragments currently
2557         # cannot be larger then VPP buffer size (which is 2048)
2558         packets = self.dst_if.get_capture(3)
2559
2560         # we should show 3 packets thru the neighbor
2561         self.assertEqual(3, nbr.get_stats()["packets"])
2562
2563         # Assume VPP sends the fragments in order
2564         payload = b""
2565         for p in packets:
2566             payload_offset = p.frag * 8
2567             if payload_offset > 0:
2568                 payload_offset -= 8  # UDP header is not in payload
2569             self.assert_equal(payload_offset, len(payload))
2570             payload += p[Raw].load
2571         self.assert_equal(payload, saved_payload, "payload")
2572
2573
2574 class TestIPReplace(VppTestCase):
2575     """IPv4 Table Replace"""
2576
2577     @classmethod
2578     def setUpClass(cls):
2579         super(TestIPReplace, cls).setUpClass()
2580
2581     @classmethod
2582     def tearDownClass(cls):
2583         super(TestIPReplace, cls).tearDownClass()
2584
2585     def setUp(self):
2586         super(TestIPReplace, self).setUp()
2587
2588         self.create_pg_interfaces(range(4))
2589
2590         table_id = 1
2591         self.tables = []
2592
2593         for i in self.pg_interfaces:
2594             i.admin_up()
2595             i.config_ip4()
2596             i.resolve_arp()
2597             i.generate_remote_hosts(2)
2598             self.tables.append(VppIpTable(self, table_id).add_vpp_config())
2599             table_id += 1
2600
2601     def tearDown(self):
2602         super(TestIPReplace, self).tearDown()
2603         for i in self.pg_interfaces:
2604             i.admin_down()
2605             i.unconfig_ip4()
2606
2607     def test_replace(self):
2608         """IP Table Replace"""
2609
2610         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
2611         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
2612         N_ROUTES = 20
2613         links = [self.pg0, self.pg1, self.pg2, self.pg3]
2614         routes = [[], [], [], []]
2615
2616         # load up the tables with some routes
2617         for ii, t in enumerate(self.tables):
2618             for jj in range(N_ROUTES):
2619                 uni = VppIpRoute(
2620                     self,
2621                     "10.0.0.%d" % jj,
2622                     32,
2623                     [
2624                         VppRoutePath(
2625                             links[ii].remote_hosts[0].ip4, links[ii].sw_if_index
2626                         ),
2627                         VppRoutePath(
2628                             links[ii].remote_hosts[1].ip4, links[ii].sw_if_index
2629                         ),
2630                     ],
2631                     table_id=t.table_id,
2632                 ).add_vpp_config()
2633                 multi = VppIpMRoute(
2634                     self,
2635                     "0.0.0.0",
2636                     "239.0.0.%d" % jj,
2637                     32,
2638                     MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
2639                     [
2640                         VppMRoutePath(
2641                             self.pg0.sw_if_index,
2642                             MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT,
2643                         ),
2644                         VppMRoutePath(
2645                             self.pg1.sw_if_index,
2646                             MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
2647                         ),
2648                         VppMRoutePath(
2649                             self.pg2.sw_if_index,
2650                             MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
2651                         ),
2652                         VppMRoutePath(
2653                             self.pg3.sw_if_index,
2654                             MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
2655                         ),
2656                     ],
2657                     table_id=t.table_id,
2658                 ).add_vpp_config()
2659                 routes[ii].append({"uni": uni, "multi": multi})
2660
2661         #
2662         # replace the tables a few times
2663         #
2664         for kk in range(3):
2665             # replace_begin each table
2666             for t in self.tables:
2667                 t.replace_begin()
2668
2669             # all the routes are still there
2670             for ii, t in enumerate(self.tables):
2671                 dump = t.dump()
2672                 mdump = t.mdump()
2673                 for r in routes[ii]:
2674                     self.assertTrue(find_route_in_dump(dump, r["uni"], t))
2675                     self.assertTrue(find_mroute_in_dump(mdump, r["multi"], t))
2676
2677             # redownload the even numbered routes
2678             for ii, t in enumerate(self.tables):
2679                 for jj in range(0, N_ROUTES, 2):
2680                     routes[ii][jj]["uni"].add_vpp_config()
2681                     routes[ii][jj]["multi"].add_vpp_config()
2682
2683             # signal each table replace_end
2684             for t in self.tables:
2685                 t.replace_end()
2686
2687             # we should find the even routes, but not the odd
2688             for ii, t in enumerate(self.tables):
2689                 dump = t.dump()
2690                 mdump = t.mdump()
2691                 for jj in range(0, N_ROUTES, 2):
2692                     self.assertTrue(find_route_in_dump(dump, routes[ii][jj]["uni"], t))
2693                     self.assertTrue(
2694                         find_mroute_in_dump(mdump, routes[ii][jj]["multi"], t)
2695                     )
2696                 for jj in range(1, N_ROUTES - 1, 2):
2697                     self.assertFalse(find_route_in_dump(dump, routes[ii][jj]["uni"], t))
2698                     self.assertFalse(
2699                         find_mroute_in_dump(mdump, routes[ii][jj]["multi"], t)
2700                     )
2701
2702             # reload all the routes
2703             for ii, t in enumerate(self.tables):
2704                 for r in routes[ii]:
2705                     r["uni"].add_vpp_config()
2706                     r["multi"].add_vpp_config()
2707
2708             # all the routes are still there
2709             for ii, t in enumerate(self.tables):
2710                 dump = t.dump()
2711                 mdump = t.mdump()
2712                 for r in routes[ii]:
2713                     self.assertTrue(find_route_in_dump(dump, r["uni"], t))
2714                     self.assertTrue(find_mroute_in_dump(mdump, r["multi"], t))
2715
2716         #
2717         # finally flush the tables for good measure
2718         #
2719         for t in self.tables:
2720             t.flush()
2721             self.assertEqual(len(t.dump()), 5)
2722             self.assertEqual(len(t.mdump()), 3)
2723
2724
2725 class TestIPCover(VppTestCase):
2726     """IPv4 Table Cover"""
2727
2728     @classmethod
2729     def setUpClass(cls):
2730         super(TestIPCover, cls).setUpClass()
2731
2732     @classmethod
2733     def tearDownClass(cls):
2734         super(TestIPCover, cls).tearDownClass()
2735
2736     def setUp(self):
2737         super(TestIPCover, self).setUp()
2738
2739         self.create_pg_interfaces(range(4))
2740
2741         table_id = 1
2742         self.tables = []
2743
2744         for i in self.pg_interfaces:
2745             i.admin_up()
2746             i.config_ip4()
2747             i.resolve_arp()
2748             i.generate_remote_hosts(2)
2749             self.tables.append(VppIpTable(self, table_id).add_vpp_config())
2750             table_id += 1
2751
2752     def tearDown(self):
2753         super(TestIPCover, self).tearDown()
2754         for i in self.pg_interfaces:
2755             i.admin_down()
2756             i.unconfig_ip4()
2757
2758     def test_cover(self):
2759         """IP Table Cover"""
2760
2761         # add a loop back with a /32 prefix
2762         lo = VppLoInterface(self)
2763         lo.admin_up()
2764         a = VppIpInterfaceAddress(self, lo, "127.0.0.1", 32).add_vpp_config()
2765
2766         # add a neighbour that matches the loopback's /32
2767         nbr = VppNeighbor(
2768             self, lo.sw_if_index, lo.remote_mac, "127.0.0.1"
2769         ).add_vpp_config()
2770
2771         # add the default route which will be the cover for /32
2772         r = VppIpRoute(
2773             self,
2774             "0.0.0.0",
2775             0,
2776             [VppRoutePath("127.0.0.1", lo.sw_if_index)],
2777             register=False,
2778         ).add_vpp_config()
2779
2780         # add/remove/add a longer mask cover
2781         r8 = VppIpRoute(
2782             self, "127.0.0.0", 8, [VppRoutePath("127.0.0.1", lo.sw_if_index)]
2783         ).add_vpp_config()
2784         r8.remove_vpp_config()
2785         r8.add_vpp_config()
2786         r8.remove_vpp_config()
2787
2788         # remove the default route
2789         r.remove_vpp_config()
2790
2791         # remove the interface prefix
2792         a.remove_vpp_config()
2793
2794
2795 class TestIP4Replace(VppTestCase):
2796     """IPv4 Interface Address Replace"""
2797
2798     @classmethod
2799     def setUpClass(cls):
2800         super(TestIP4Replace, cls).setUpClass()
2801
2802     @classmethod
2803     def tearDownClass(cls):
2804         super(TestIP4Replace, cls).tearDownClass()
2805
2806     def setUp(self):
2807         super(TestIP4Replace, self).setUp()
2808
2809         self.create_pg_interfaces(range(4))
2810
2811         for i in self.pg_interfaces:
2812             i.admin_up()
2813
2814     def tearDown(self):
2815         super(TestIP4Replace, self).tearDown()
2816         for i in self.pg_interfaces:
2817             i.admin_down()
2818
2819     def get_n_pfxs(self, intf):
2820         return len(self.vapi.ip_address_dump(intf.sw_if_index))
2821
2822     def test_replace(self):
2823         """IP interface address replace"""
2824
2825         intf_pfxs = [[], [], [], []]
2826
2827         # add prefixes to each of the interfaces
2828         for i in range(len(self.pg_interfaces)):
2829             intf = self.pg_interfaces[i]
2830
2831             # 172.16.x.1/24
2832             addr = "172.16.%d.1" % intf.sw_if_index
2833             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2834             intf_pfxs[i].append(a)
2835
2836             # 172.16.x.2/24 - a different address in the same subnet as above
2837             addr = "172.16.%d.2" % intf.sw_if_index
2838             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2839             intf_pfxs[i].append(a)
2840
2841             # 172.15.x.2/24 - a different address and subnet
2842             addr = "172.15.%d.2" % intf.sw_if_index
2843             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2844             intf_pfxs[i].append(a)
2845
2846         # a dump should n_address in it
2847         for intf in self.pg_interfaces:
2848             self.assertEqual(self.get_n_pfxs(intf), 3)
2849
2850         #
2851         # remove all the address thru a replace
2852         #
2853         self.vapi.sw_interface_address_replace_begin()
2854         self.vapi.sw_interface_address_replace_end()
2855         for intf in self.pg_interfaces:
2856             self.assertEqual(self.get_n_pfxs(intf), 0)
2857
2858         #
2859         # add all the interface addresses back
2860         #
2861         for p in intf_pfxs:
2862             for v in p:
2863                 v.add_vpp_config()
2864         for intf in self.pg_interfaces:
2865             self.assertEqual(self.get_n_pfxs(intf), 3)
2866
2867         #
2868         # replace again, but this time update/re-add the address on the first
2869         # two interfaces
2870         #
2871         self.vapi.sw_interface_address_replace_begin()
2872
2873         for p in intf_pfxs[:2]:
2874             for v in p:
2875                 v.add_vpp_config()
2876
2877         self.vapi.sw_interface_address_replace_end()
2878
2879         # on the first two the address still exist,
2880         # on the other two they do not
2881         for intf in self.pg_interfaces[:2]:
2882             self.assertEqual(self.get_n_pfxs(intf), 3)
2883         for p in intf_pfxs[:2]:
2884             for v in p:
2885                 self.assertTrue(v.query_vpp_config())
2886         for intf in self.pg_interfaces[2:]:
2887             self.assertEqual(self.get_n_pfxs(intf), 0)
2888
2889         #
2890         # add all the interface addresses back on the last two
2891         #
2892         for p in intf_pfxs[2:]:
2893             for v in p:
2894                 v.add_vpp_config()
2895         for intf in self.pg_interfaces:
2896             self.assertEqual(self.get_n_pfxs(intf), 3)
2897
2898         #
2899         # replace again, this time add different prefixes on all the interfaces
2900         #
2901         self.vapi.sw_interface_address_replace_begin()
2902
2903         pfxs = []
2904         for intf in self.pg_interfaces:
2905             # 172.18.x.1/24
2906             addr = "172.18.%d.1" % intf.sw_if_index
2907             pfxs.append(VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config())
2908
2909         self.vapi.sw_interface_address_replace_end()
2910
2911         # only .18 should exist on each interface
2912         for intf in self.pg_interfaces:
2913             self.assertEqual(self.get_n_pfxs(intf), 1)
2914         for pfx in pfxs:
2915             self.assertTrue(pfx.query_vpp_config())
2916
2917         #
2918         # remove everything
2919         #
2920         self.vapi.sw_interface_address_replace_begin()
2921         self.vapi.sw_interface_address_replace_end()
2922         for intf in self.pg_interfaces:
2923             self.assertEqual(self.get_n_pfxs(intf), 0)
2924
2925         #
2926         # add prefixes to each interface. post-begin add the prefix from
2927         # interface X onto interface Y. this would normally be an error
2928         # since it would generate a 'duplicate address' warning. but in
2929         # this case, since what is newly downloaded is sane, it's ok
2930         #
2931         for intf in self.pg_interfaces:
2932             # 172.18.x.1/24
2933             addr = "172.18.%d.1" % intf.sw_if_index
2934             VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2935
2936         self.vapi.sw_interface_address_replace_begin()
2937
2938         pfxs = []
2939         for intf in self.pg_interfaces:
2940             # 172.18.x.1/24
2941             addr = "172.18.%d.1" % (intf.sw_if_index + 1)
2942             pfxs.append(VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config())
2943
2944         self.vapi.sw_interface_address_replace_end()
2945
2946         self.logger.info(self.vapi.cli("sh int addr"))
2947
2948         for intf in self.pg_interfaces:
2949             self.assertEqual(self.get_n_pfxs(intf), 1)
2950         for pfx in pfxs:
2951             self.assertTrue(pfx.query_vpp_config())
2952
2953
2954 class TestIPv4PathMTU(VppTestCase):
2955     """IPv4 Path MTU"""
2956
2957     @classmethod
2958     def setUpClass(cls):
2959         super(TestIPv4PathMTU, cls).setUpClass()
2960
2961         cls.create_pg_interfaces(range(2))
2962
2963         # setup all interfaces
2964         for i in cls.pg_interfaces:
2965             i.admin_up()
2966             i.config_ip4()
2967             i.resolve_arp()
2968
2969     @classmethod
2970     def tearDownClass(cls):
2971         super(TestIPv4PathMTU, cls).tearDownClass()
2972
2973     def test_path_mtu(self):
2974         """Path MTU"""
2975
2976         #
2977         # The goal here is not to test that fragmentation works correctly,
2978         # that's done elsewhere, the intent is to ensure that the Path MTU
2979         # settings are honoured.
2980         #
2981         self.vapi.cli("adjacency counters enable")
2982
2983         # set the interface MTU to a reasonable value
2984         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1800, 0, 0, 0])
2985
2986         self.pg1.generate_remote_hosts(4)
2987
2988         p_2k = (
2989             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2990             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4)
2991             / UDP(sport=1234, dport=5678)
2992             / Raw(b"0xa" * 640)
2993         )
2994         p_1k = (
2995             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2996             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4)
2997             / UDP(sport=1234, dport=5678)
2998             / Raw(b"0xa" * 320)
2999         )
3000
3001         nbr = VppNeighbor(
3002             self, self.pg1.sw_if_index, self.pg1.remote_mac, self.pg1.remote_ip4
3003         ).add_vpp_config()
3004
3005         # this is now the interface MTU frags
3006         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=2)
3007         self.send_and_expect(self.pg0, [p_1k], self.pg1)
3008
3009         # drop the path MTU for this neighbour to below the interface MTU
3010         # expect more frags
3011         pmtu = VppIpPathMtu(self, self.pg1.remote_ip4, 900).add_vpp_config()
3012
3013         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3014         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3015
3016         # print/format the adj delegate
3017         self.logger.info(self.vapi.cli("sh adj 5"))
3018
3019         # increase the path MTU to more than the interface
3020         # expect to use the interface MTU
3021         pmtu.modify(8192)
3022
3023         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=2)
3024         self.send_and_expect(self.pg0, [p_1k], self.pg1)
3025
3026         # go back to an MTU from the path
3027         # wrap the call around mark-n-sweep to enusre updates clear stale
3028         self.vapi.ip_path_mtu_replace_begin()
3029         pmtu.modify(900)
3030         self.vapi.ip_path_mtu_replace_end()
3031
3032         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3033         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3034
3035         # raise the interface's MTU
3036         # should still use that of the path
3037         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [2000, 0, 0, 0])
3038         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3039         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3040
3041         # set path high and interface low
3042         pmtu.modify(2000)
3043         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [900, 0, 0, 0])
3044         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3045         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3046
3047         # remove the path MTU using the mark-n-sweep semantics
3048         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1800, 0, 0, 0])
3049         self.vapi.ip_path_mtu_replace_begin()
3050         self.vapi.ip_path_mtu_replace_end()
3051
3052         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=2)
3053         self.send_and_expect(self.pg0, [p_1k], self.pg1)
3054
3055         #
3056         # set path MTU for a neighbour that doesn't exist, yet
3057         #
3058         pmtu2 = VppIpPathMtu(self, self.pg1.remote_hosts[2].ip4, 900).add_vpp_config()
3059
3060         p_2k = (
3061             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
3062             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_hosts[2].ip4)
3063             / UDP(sport=1234, dport=5678)
3064             / Raw(b"0xa" * 640)
3065         )
3066         p_1k = (
3067             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
3068             / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_hosts[2].ip4)
3069             / UDP(sport=1234, dport=5678)
3070             / Raw(b"0xa" * 320)
3071         )
3072
3073         nbr2 = VppNeighbor(
3074             self,
3075             self.pg1.sw_if_index,
3076             self.pg1.remote_hosts[2].mac,
3077             self.pg1.remote_hosts[2].ip4,
3078         ).add_vpp_config()
3079
3080         # should frag to the path MTU
3081         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3082         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3083
3084         # remove and re-add the neighbour
3085         nbr2.remove_vpp_config()
3086         nbr2.add_vpp_config()
3087
3088         # should frag to the path MTU
3089         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3090         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3091
3092         #
3093         # set PMTUs for many peers
3094         #
3095         N_HOSTS = 16
3096         self.pg1.generate_remote_hosts(16)
3097         self.pg1.configure_ipv4_neighbors()
3098
3099         for h in range(N_HOSTS):
3100             pmtu = VppIpPathMtu(self, self.pg1.remote_hosts[h].ip4, 900)
3101             pmtu.add_vpp_config()
3102             self.assertTrue(pmtu.query_vpp_config())
3103
3104         self.logger.info(self.vapi.cli("sh ip pmtu"))
3105         dump = list(self.vapi.vpp.details_iter(self.vapi.ip_path_mtu_get))
3106         self.assertEqual(N_HOSTS, len(dump))
3107
3108         for h in range(N_HOSTS):
3109             p_2k[IP].dst = self.pg1.remote_hosts[h].ip4
3110             p_1k[IP].dst = self.pg1.remote_hosts[h].ip4
3111
3112             # should frag to the path MTU
3113             self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
3114             self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
3115
3116
3117 class TestIPv4ItfRebind(VppTestCase):
3118     """IPv4 Interface Bind w/ attached routes"""
3119
3120     def setUp(self):
3121         super(TestIPv4ItfRebind, self).setUp()
3122
3123         self.create_pg_interfaces(range(3))
3124
3125     def tearDown(self):
3126         super(TestIPv4ItfRebind, self).tearDown()
3127
3128     def test_rebind(self):
3129         """Import to no import"""
3130
3131         TABLE_ID = 1
3132         tbl = VppIpTable(self, TABLE_ID).add_vpp_config()
3133         self.pg1.set_table_ip4(TABLE_ID)
3134
3135         for i in self.pg_interfaces:
3136             i.admin_up()
3137             i.config_ip4()
3138             i.resolve_arp()
3139
3140         # add an attached route via an pg0
3141         # in a different table. this prefix should import
3142         rt = VppIpRoute(
3143             self,
3144             self.pg0.local_ip4,
3145             24,
3146             [VppRoutePath("0.0.0.0", self.pg0.sw_if_index)],
3147             table_id=TABLE_ID,
3148         ).add_vpp_config()
3149
3150         p = (
3151             Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
3152             / IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4)
3153             / UDP(sport=1234, dport=5678)
3154             / Raw(b"0xa" * 640)
3155         )
3156
3157         rx = self.send_and_expect(self.pg1, [p], self.pg0)
3158         self.assertFalse(rx[0].haslayer(ARP))
3159
3160         # then bind pg0 to a new table
3161         # so the prefix no longer imports
3162         self.pg0.unconfig_ip4()
3163         self.pg0.set_table_ip4(TABLE_ID)
3164         self.pg0.config_ip4()
3165         self.pg0.resolve_arp()
3166
3167         rx = self.send_and_expect(self.pg1, [p], self.pg0)
3168         self.assertFalse(rx[0].haslayer(ARP))
3169
3170         # revert back to imported
3171         self.pg0.unconfig_ip4()
3172         self.pg0.set_table_ip4(0)
3173         self.pg0.config_ip4()
3174         self.pg0.resolve_arp()
3175
3176         rx = self.send_and_expect(self.pg1, [p], self.pg0)
3177         self.assertFalse(rx[0].haslayer(ARP))
3178
3179         # cleanup
3180         for i in self.pg_interfaces:
3181             i.unconfig_ip4()
3182             i.set_table_ip4(0)
3183             i.admin_down()
3184
3185         rt.remove_vpp_config()
3186         tbl.remove_vpp_config()
3187
3188     def test_delete(self):
3189         """Swap import tables"""
3190
3191         TABLE_ID1 = 1
3192         tbl1_4 = VppIpTable(self, TABLE_ID1).add_vpp_config()
3193         tbl1_6 = VppIpTable(self, TABLE_ID1, True).add_vpp_config()
3194         TABLE_ID2 = 2
3195         tbl2_4 = VppIpTable(self, TABLE_ID2).add_vpp_config()
3196         tbl2_6 = VppIpTable(self, TABLE_ID2, True).add_vpp_config()
3197
3198         # table mappings
3199         self.pg1.set_table_ip4(TABLE_ID1)
3200         self.pg1.set_table_ip6(TABLE_ID1)
3201         self.pg2.set_table_ip4(TABLE_ID2)
3202         self.pg2.set_table_ip6(TABLE_ID2)
3203
3204         for i in self.pg_interfaces:
3205             i.admin_up()
3206             i.config_ip4()
3207             i.resolve_arp()
3208
3209         # add an attached route in the default table via pg0
3210         # this should import to table 1
3211         rt4 = VppIpRoute(
3212             self,
3213             self.pg1.local_ip4,
3214             24,
3215             [VppRoutePath("0.0.0.0", self.pg1.sw_if_index)],
3216         ).add_vpp_config()
3217         rt6 = VppIpRoute(
3218             self,
3219             self.pg1.local_ip6,
3220             64,
3221             [VppRoutePath("0.0.0.0", self.pg1.sw_if_index)],
3222         ).add_vpp_config()
3223
3224         p1 = (
3225             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
3226             / IP(src=self.pg1.remote_ip4, dst=self.pg1.remote_ip4)
3227             / UDP(sport=1234, dport=5678)
3228             / Raw(b"0xa" * 640)
3229         )
3230
3231         # inject into table 0
3232         rx = self.send_and_expect(self.pg0, [p1], self.pg1)
3233         self.assertFalse(rx[0].haslayer(ARP))
3234
3235         # swap the attached interface to table 2
3236         self.pg1.unconfig_ip4()
3237         self.pg1.unconfig_ip6()
3238         self.pg1.set_table_ip4(TABLE_ID2)
3239         self.pg1.set_table_ip6(TABLE_ID2)
3240         self.pg1.config_ip4()
3241         self.pg1.config_ip6()
3242         self.pg1.resolve_arp()
3243
3244         # delete table 1
3245         tbl1_4.flush()
3246         tbl1_6.flush()
3247         tbl1_4.remove_vpp_config()
3248         tbl1_6.remove_vpp_config()
3249
3250         rx = self.send_and_expect(self.pg0, [p1], self.pg1)
3251         self.assertFalse(rx[0].haslayer(ARP))
3252
3253         for i in self.pg_interfaces:
3254             i.unconfig_ip4()
3255             i.unconfig_ip6()
3256             i.set_table_ip4(0)
3257             i.set_table_ip6(0)
3258             i.admin_down()
3259
3260
3261 if __name__ == "__main__":
3262     unittest.main(testRunner=VppTestRunner)