xref: /freebsd/sys/tests/fib_lookup/fib_lookup.c (revision b8598e2ff65ab82da0cf6861ee12f078b40bc252)
10433870eSAlexander V. Chernikov /*-
20433870eSAlexander V. Chernikov  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
30433870eSAlexander V. Chernikov  *
40433870eSAlexander V. Chernikov  * Copyright (c) 2020 Alexander V. Chernikov
50433870eSAlexander V. Chernikov  *
60433870eSAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
70433870eSAlexander V. Chernikov  * modification, are permitted provided that the following conditions
80433870eSAlexander V. Chernikov  * are met:
90433870eSAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
100433870eSAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
110433870eSAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
120433870eSAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
130433870eSAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
140433870eSAlexander V. Chernikov  *
150433870eSAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
160433870eSAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
170433870eSAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
180433870eSAlexander V. Chernikov  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
190433870eSAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
200433870eSAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
210433870eSAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
220433870eSAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
230433870eSAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
240433870eSAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
250433870eSAlexander V. Chernikov  * SUCH DAMAGE.
260433870eSAlexander V. Chernikov  */
270433870eSAlexander V. Chernikov 
280433870eSAlexander V. Chernikov #include <sys/cdefs.h>
290433870eSAlexander V. Chernikov __FBSDID("$FreeBSD$");
300433870eSAlexander V. Chernikov #include "opt_inet.h"
310433870eSAlexander V. Chernikov #include "opt_inet6.h"
320433870eSAlexander V. Chernikov 
330433870eSAlexander V. Chernikov #include <sys/param.h>
340433870eSAlexander V. Chernikov #include <sys/kernel.h>
350433870eSAlexander V. Chernikov #include <sys/lock.h>
360433870eSAlexander V. Chernikov #include <sys/rmlock.h>
370433870eSAlexander V. Chernikov #include <sys/malloc.h>
380433870eSAlexander V. Chernikov #include <sys/module.h>
390433870eSAlexander V. Chernikov #include <sys/kernel.h>
400433870eSAlexander V. Chernikov #include <sys/socket.h>
410433870eSAlexander V. Chernikov #include <sys/sysctl.h>
420433870eSAlexander V. Chernikov #include <net/vnet.h>
430433870eSAlexander V. Chernikov 
440433870eSAlexander V. Chernikov #include <net/if.h>
450433870eSAlexander V. Chernikov #include <net/if_var.h>
460433870eSAlexander V. Chernikov 
470433870eSAlexander V. Chernikov #include <netinet/in.h>
480433870eSAlexander V. Chernikov #include <netinet/in_fib.h>
490433870eSAlexander V. Chernikov #include <netinet/ip.h>
500433870eSAlexander V. Chernikov 
510433870eSAlexander V. Chernikov #include <netinet6/in6_fib.h>
520433870eSAlexander V. Chernikov 
530433870eSAlexander V. Chernikov #include <net/route.h>
540433870eSAlexander V. Chernikov #include <net/route/nhop.h>
550433870eSAlexander V. Chernikov #include <net/route/route_ctl.h>
560433870eSAlexander V. Chernikov #include <net/route/route_var.h>
570433870eSAlexander V. Chernikov #include <net/route/fib_algo.h>
580433870eSAlexander V. Chernikov 
590433870eSAlexander V. Chernikov #define	CHUNK_SIZE	10000
600433870eSAlexander V. Chernikov 
610433870eSAlexander V. Chernikov VNET_DEFINE_STATIC(struct in_addr *, inet_addr_list);
620433870eSAlexander V. Chernikov #define	V_inet_addr_list	VNET(inet_addr_list)
630433870eSAlexander V. Chernikov VNET_DEFINE_STATIC(int, inet_list_size);
640433870eSAlexander V. Chernikov #define	V_inet_list_size	VNET(inet_list_size)
650433870eSAlexander V. Chernikov 
660433870eSAlexander V. Chernikov VNET_DEFINE_STATIC(struct in6_addr *, inet6_addr_list);
670433870eSAlexander V. Chernikov #define	V_inet6_addr_list	VNET(inet6_addr_list)
680433870eSAlexander V. Chernikov VNET_DEFINE_STATIC(int, inet6_list_size);
690433870eSAlexander V. Chernikov #define	V_inet6_list_size	VNET(inet6_list_size)
700433870eSAlexander V. Chernikov 
710433870eSAlexander V. Chernikov SYSCTL_DECL(_net_route);
720433870eSAlexander V. Chernikov SYSCTL_NODE(_net_route, OID_AUTO, test, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
730433870eSAlexander V. Chernikov     "Route algorithm lookups");
740433870eSAlexander V. Chernikov 
750433870eSAlexander V. Chernikov static int
760433870eSAlexander V. Chernikov add_addr(int family, char *addr_str)
770433870eSAlexander V. Chernikov {
780433870eSAlexander V. Chernikov 
790433870eSAlexander V. Chernikov 	if (family == AF_INET) {
800433870eSAlexander V. Chernikov 		struct in_addr *paddr_old = V_inet_addr_list;
810433870eSAlexander V. Chernikov 		int size_old = V_inet_list_size;
820433870eSAlexander V. Chernikov 		struct in_addr addr;
830433870eSAlexander V. Chernikov 
840433870eSAlexander V. Chernikov 		if (inet_pton(AF_INET, addr_str, &addr) != 1)
850433870eSAlexander V. Chernikov 			return (EINVAL);
860433870eSAlexander V. Chernikov 
870433870eSAlexander V. Chernikov 		struct in_addr *paddr = mallocarray(size_old + 1,
880433870eSAlexander V. Chernikov 		    sizeof(struct in_addr), M_TEMP, M_ZERO | M_WAITOK);
890433870eSAlexander V. Chernikov 
900433870eSAlexander V. Chernikov 		if (paddr_old != NULL) {
910433870eSAlexander V. Chernikov 			memcpy(paddr, paddr_old, size_old * sizeof(struct in_addr));
920433870eSAlexander V. Chernikov 			free(paddr_old, M_TEMP);
930433870eSAlexander V. Chernikov 		}
940433870eSAlexander V. Chernikov 		paddr[size_old] = addr;
950433870eSAlexander V. Chernikov 
960433870eSAlexander V. Chernikov 		V_inet_addr_list = paddr;
970433870eSAlexander V. Chernikov 		V_inet_list_size = size_old + 1;
980433870eSAlexander V. Chernikov 		inet_ntop(AF_INET, &addr, addr_str, sizeof(addr_str));
990433870eSAlexander V. Chernikov 	} else if (family == AF_INET6) {
1000433870eSAlexander V. Chernikov 		struct in6_addr *paddr_old = V_inet6_addr_list;
1010433870eSAlexander V. Chernikov 		int size_old = V_inet6_list_size;
1020433870eSAlexander V. Chernikov 		struct in6_addr addr6;
1030433870eSAlexander V. Chernikov 
1040433870eSAlexander V. Chernikov 		if (inet_pton(AF_INET6, addr_str, &addr6) != 1)
1050433870eSAlexander V. Chernikov 			return (EINVAL);
1060433870eSAlexander V. Chernikov 
1070433870eSAlexander V. Chernikov 		struct in6_addr *paddr = mallocarray(size_old + 1,
1080433870eSAlexander V. Chernikov 		    sizeof(struct in6_addr), M_TEMP, M_ZERO | M_WAITOK);
1090433870eSAlexander V. Chernikov 
1100433870eSAlexander V. Chernikov 		if (paddr_old != NULL) {
1110433870eSAlexander V. Chernikov 			memcpy(paddr, paddr_old, size_old * sizeof(struct in6_addr));
1120433870eSAlexander V. Chernikov 			free(paddr_old, M_TEMP);
1130433870eSAlexander V. Chernikov 		}
1140433870eSAlexander V. Chernikov 		paddr[size_old] = addr6;
1150433870eSAlexander V. Chernikov 
1160433870eSAlexander V. Chernikov 		V_inet6_addr_list = paddr;
1170433870eSAlexander V. Chernikov 		V_inet6_list_size = size_old + 1;
1180433870eSAlexander V. Chernikov 		inet_ntop(AF_INET6, &addr6, addr_str, sizeof(addr_str));
1190433870eSAlexander V. Chernikov 	}
1200433870eSAlexander V. Chernikov 
1210433870eSAlexander V. Chernikov 	return (0);
1220433870eSAlexander V. Chernikov }
1230433870eSAlexander V. Chernikov 
1240433870eSAlexander V. Chernikov static int
1250433870eSAlexander V. Chernikov add_addr_sysctl_handler(struct sysctl_oid *oidp, struct sysctl_req *req, int family)
1260433870eSAlexander V. Chernikov {
1270433870eSAlexander V. Chernikov 	char addr_str[INET6_ADDRSTRLEN];
1280433870eSAlexander V. Chernikov 	int error;
1290433870eSAlexander V. Chernikov 
1300433870eSAlexander V. Chernikov 	bzero(addr_str, sizeof(addr_str));
1310433870eSAlexander V. Chernikov 
1320433870eSAlexander V. Chernikov 	error = sysctl_handle_string(oidp, addr_str, sizeof(addr_str), req);
1330433870eSAlexander V. Chernikov 	if (error != 0 || req->newptr == NULL)
1340433870eSAlexander V. Chernikov 		return (error);
1350433870eSAlexander V. Chernikov 
1360433870eSAlexander V. Chernikov 	error = add_addr(family, addr_str);
1370433870eSAlexander V. Chernikov 
1380433870eSAlexander V. Chernikov 	return (0);
1390433870eSAlexander V. Chernikov }
1400433870eSAlexander V. Chernikov 
1410433870eSAlexander V. Chernikov static int
1420433870eSAlexander V. Chernikov add_inet_addr_sysctl_handler(SYSCTL_HANDLER_ARGS)
1430433870eSAlexander V. Chernikov {
1440433870eSAlexander V. Chernikov 
1450433870eSAlexander V. Chernikov 	return (add_addr_sysctl_handler(oidp, req, AF_INET));
1460433870eSAlexander V. Chernikov }
1470433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, add_inet_addr,
1480433870eSAlexander V. Chernikov     CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
1490433870eSAlexander V. Chernikov     add_inet_addr_sysctl_handler, "A", "Set");
1500433870eSAlexander V. Chernikov 
1510433870eSAlexander V. Chernikov static int
1520433870eSAlexander V. Chernikov add_inet6_addr_sysctl_handler(SYSCTL_HANDLER_ARGS)
1530433870eSAlexander V. Chernikov {
1540433870eSAlexander V. Chernikov 
1550433870eSAlexander V. Chernikov 	return (add_addr_sysctl_handler(oidp, req, AF_INET6));
1560433870eSAlexander V. Chernikov }
1570433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, add_inet6_addr,
1580433870eSAlexander V. Chernikov     CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
1590433870eSAlexander V. Chernikov     add_inet6_addr_sysctl_handler, "A", "Set");
1600433870eSAlexander V. Chernikov 
1610433870eSAlexander V. Chernikov static uint64_t
1620433870eSAlexander V. Chernikov run_test_inet_one_pass()
1630433870eSAlexander V. Chernikov {
1640433870eSAlexander V. Chernikov 	/* Assume epoch */
1650433870eSAlexander V. Chernikov 	int sz = V_inet_list_size;
1660433870eSAlexander V. Chernikov 	int tries = CHUNK_SIZE / sz;
1670433870eSAlexander V. Chernikov 	const struct in_addr *a = V_inet_addr_list;
1680433870eSAlexander V. Chernikov 	uint64_t count = 0;
1690433870eSAlexander V. Chernikov 
1700433870eSAlexander V. Chernikov 	for (int pass = 0; pass < tries; pass++) {
1710433870eSAlexander V. Chernikov 		for (int i = 0; i < sz; i++) {
1720433870eSAlexander V. Chernikov 			fib4_lookup(RT_DEFAULT_FIB, a[i], 0, NHR_NONE, 0);
1730433870eSAlexander V. Chernikov 			count++;
1740433870eSAlexander V. Chernikov 		}
1750433870eSAlexander V. Chernikov 	}
1760433870eSAlexander V. Chernikov 	return (count);
1770433870eSAlexander V. Chernikov }
1780433870eSAlexander V. Chernikov 
1790433870eSAlexander V. Chernikov static int
1800433870eSAlexander V. Chernikov run_test_inet(SYSCTL_HANDLER_ARGS)
1810433870eSAlexander V. Chernikov {
1820433870eSAlexander V. Chernikov 	struct epoch_tracker et;
1830433870eSAlexander V. Chernikov 
1840433870eSAlexander V. Chernikov 	int count = 0;
1850433870eSAlexander V. Chernikov 	int error = sysctl_handle_int(oidp, &count, 0, req);
1860433870eSAlexander V. Chernikov 	if (error != 0)
1870433870eSAlexander V. Chernikov 		return (error);
1880433870eSAlexander V. Chernikov 
1890433870eSAlexander V. Chernikov 	if (count == 0)
1900433870eSAlexander V. Chernikov 		return (0);
1910433870eSAlexander V. Chernikov 
1920433870eSAlexander V. Chernikov 	if (V_inet_list_size <= 0)
1930433870eSAlexander V. Chernikov 		return (ENOENT);
1940433870eSAlexander V. Chernikov 
1950433870eSAlexander V. Chernikov 	printf("run: %d packets vnet %p\n", count, curvnet);
1960433870eSAlexander V. Chernikov 	if (count < CHUNK_SIZE)
1970433870eSAlexander V. Chernikov 		count = CHUNK_SIZE;
1980433870eSAlexander V. Chernikov 
1990433870eSAlexander V. Chernikov 	struct timespec ts_pre, ts_post;
2000433870eSAlexander V. Chernikov 	int64_t pass_diff, total_diff = 0;
2010433870eSAlexander V. Chernikov 	uint64_t pass_packets, total_packets = 0;
2020433870eSAlexander V. Chernikov 
2030433870eSAlexander V. Chernikov 	for (int pass = 0; pass < count / CHUNK_SIZE; pass++) {
2040433870eSAlexander V. Chernikov 		NET_EPOCH_ENTER(et);
2050433870eSAlexander V. Chernikov 		nanouptime(&ts_pre);
2060433870eSAlexander V. Chernikov 		pass_packets = run_test_inet_one_pass();
2070433870eSAlexander V. Chernikov 		nanouptime(&ts_post);
2080433870eSAlexander V. Chernikov 		NET_EPOCH_EXIT(et);
2090433870eSAlexander V. Chernikov 
2100433870eSAlexander V. Chernikov 		pass_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
2110433870eSAlexander V. Chernikov 		    (ts_post.tv_nsec - ts_pre.tv_nsec);
2120433870eSAlexander V. Chernikov 		total_diff += pass_diff;
2130433870eSAlexander V. Chernikov 		total_packets += pass_packets;
2140433870eSAlexander V. Chernikov 	}
2150433870eSAlexander V. Chernikov 
2160433870eSAlexander V. Chernikov 	printf("%zu packets in %zu nanoseconds, %zu pps\n",
2170433870eSAlexander V. Chernikov 	    total_packets, total_diff, total_packets * 1000000000 / total_diff);
2180433870eSAlexander V. Chernikov 
2190433870eSAlexander V. Chernikov 	return (0);
2200433870eSAlexander V. Chernikov }
2210433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet,
22274935ce8SAlexander V. Chernikov     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
2230433870eSAlexander V. Chernikov     0, 0, run_test_inet, "I", "Execute fib4_lookup test");
2240433870eSAlexander V. Chernikov 
2250433870eSAlexander V. Chernikov static uint64_t
2260433870eSAlexander V. Chernikov run_test_inet6_one_pass()
2270433870eSAlexander V. Chernikov {
2280433870eSAlexander V. Chernikov 	/* Assume epoch */
2290433870eSAlexander V. Chernikov 	int sz = V_inet6_list_size;
2300433870eSAlexander V. Chernikov 	int tries = CHUNK_SIZE / sz;
2310433870eSAlexander V. Chernikov 	const struct in6_addr *a = V_inet6_addr_list;
2320433870eSAlexander V. Chernikov 	uint64_t count = 0;
2330433870eSAlexander V. Chernikov 
2340433870eSAlexander V. Chernikov 	for (int pass = 0; pass < tries; pass++) {
2350433870eSAlexander V. Chernikov 		for (int i = 0; i < sz; i++) {
2360433870eSAlexander V. Chernikov 			fib6_lookup(RT_DEFAULT_FIB, &a[i], 0, NHR_NONE, 0);
2370433870eSAlexander V. Chernikov 			count++;
2380433870eSAlexander V. Chernikov 		}
2390433870eSAlexander V. Chernikov 	}
2400433870eSAlexander V. Chernikov 	return (count);
2410433870eSAlexander V. Chernikov }
2420433870eSAlexander V. Chernikov 
2430433870eSAlexander V. Chernikov static int
2440433870eSAlexander V. Chernikov run_test_inet6(SYSCTL_HANDLER_ARGS)
2450433870eSAlexander V. Chernikov {
2460433870eSAlexander V. Chernikov 	struct epoch_tracker et;
2470433870eSAlexander V. Chernikov 
2480433870eSAlexander V. Chernikov 	int count = 0;
2490433870eSAlexander V. Chernikov 	int error = sysctl_handle_int(oidp, &count, 0, req);
2500433870eSAlexander V. Chernikov 	if (error != 0)
2510433870eSAlexander V. Chernikov 		return (error);
2520433870eSAlexander V. Chernikov 
2530433870eSAlexander V. Chernikov 	if (count == 0)
2540433870eSAlexander V. Chernikov 		return (0);
2550433870eSAlexander V. Chernikov 
2560433870eSAlexander V. Chernikov 	if (V_inet6_list_size <= 0)
2570433870eSAlexander V. Chernikov 		return (ENOENT);
2580433870eSAlexander V. Chernikov 
2590433870eSAlexander V. Chernikov 	printf("run: %d packets vnet %p\n", count, curvnet);
2600433870eSAlexander V. Chernikov 	if (count < CHUNK_SIZE)
2610433870eSAlexander V. Chernikov 		count = CHUNK_SIZE;
2620433870eSAlexander V. Chernikov 
2630433870eSAlexander V. Chernikov 	struct timespec ts_pre, ts_post;
2640433870eSAlexander V. Chernikov 	int64_t pass_diff, total_diff = 0;
2650433870eSAlexander V. Chernikov 	uint64_t pass_packets, total_packets = 0;
2660433870eSAlexander V. Chernikov 
2670433870eSAlexander V. Chernikov 	for (int pass = 0; pass < count / CHUNK_SIZE; pass++) {
2680433870eSAlexander V. Chernikov 		NET_EPOCH_ENTER(et);
2690433870eSAlexander V. Chernikov 		nanouptime(&ts_pre);
2700433870eSAlexander V. Chernikov 		pass_packets = run_test_inet6_one_pass();
2710433870eSAlexander V. Chernikov 		nanouptime(&ts_post);
2720433870eSAlexander V. Chernikov 		NET_EPOCH_EXIT(et);
2730433870eSAlexander V. Chernikov 
2740433870eSAlexander V. Chernikov 		pass_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
2750433870eSAlexander V. Chernikov 		    (ts_post.tv_nsec - ts_pre.tv_nsec);
2760433870eSAlexander V. Chernikov 		total_diff += pass_diff;
2770433870eSAlexander V. Chernikov 		total_packets += pass_packets;
2780433870eSAlexander V. Chernikov 	}
2790433870eSAlexander V. Chernikov 
2800433870eSAlexander V. Chernikov 	printf("%zu packets in %zu nanoseconds, %zu pps\n",
2810433870eSAlexander V. Chernikov 	    total_packets, total_diff, total_packets * 1000000000 / total_diff);
2820433870eSAlexander V. Chernikov 
2830433870eSAlexander V. Chernikov 	return (0);
2840433870eSAlexander V. Chernikov }
2850433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet6,
28674935ce8SAlexander V. Chernikov     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
2870433870eSAlexander V. Chernikov     0, 0, run_test_inet6, "I", "Execute fib6_lookup test");
2880433870eSAlexander V. Chernikov 
2890433870eSAlexander V. Chernikov static bool
2900433870eSAlexander V. Chernikov cmp_dst(uint32_t fibnum, struct in_addr a)
2910433870eSAlexander V. Chernikov {
2920433870eSAlexander V. Chernikov 	struct nhop_object *nh_fib;
2930433870eSAlexander V. Chernikov 	struct rtentry *rt;
2940433870eSAlexander V. Chernikov 	struct route_nhop_data rnd = {};
2950433870eSAlexander V. Chernikov 
2960433870eSAlexander V. Chernikov 	nh_fib = fib4_lookup(fibnum, a, 0, NHR_NONE, 0);
2970433870eSAlexander V. Chernikov 	rt = fib4_lookup_rt(fibnum, a, 0, NHR_NONE, &rnd);
2980433870eSAlexander V. Chernikov 
2990433870eSAlexander V. Chernikov 	if (nh_fib == NULL && rt == NULL) {
3000433870eSAlexander V. Chernikov 		return (true);
3010433870eSAlexander V. Chernikov 	} else if (nh_fib == nhop_select(rnd.rnd_nhop, 0)) {
3020433870eSAlexander V. Chernikov 		return (true);
3030433870eSAlexander V. Chernikov 	}
3040433870eSAlexander V. Chernikov 
3050433870eSAlexander V. Chernikov 	struct in_addr dst;
3060433870eSAlexander V. Chernikov 	int plen;
3070433870eSAlexander V. Chernikov 	uint32_t scopeid;
3080433870eSAlexander V. Chernikov 	char key_str[INET_ADDRSTRLEN], dst_str[INET_ADDRSTRLEN];
3090433870eSAlexander V. Chernikov 
3100433870eSAlexander V. Chernikov 	inet_ntop(AF_INET, &a, key_str, sizeof(key_str));
3110433870eSAlexander V. Chernikov 	if (rnd.rnd_nhop == NULL) {
3120433870eSAlexander V. Chernikov 		printf("[RT BUG] lookup for %s: RIB: ENOENT FIB: nh=%u\n",
3130433870eSAlexander V. Chernikov 		    key_str, nhop_get_idx(nh_fib));
3140433870eSAlexander V. Chernikov 	} else {
3150433870eSAlexander V. Chernikov 		rt_get_inet_prefix_plen(rt, &dst, &plen, &scopeid);
3160433870eSAlexander V. Chernikov 		inet_ntop(AF_INET, &dst, dst_str, sizeof(dst_str));
3170433870eSAlexander V. Chernikov 		printf("[RT BUG] lookup for %s: RIB: %s/%d,nh=%u FIB: nh=%u\n",
3180433870eSAlexander V. Chernikov 		    key_str, dst_str, plen,
3190433870eSAlexander V. Chernikov 		    nhop_get_idx(nhop_select(rnd.rnd_nhop, 0)),
3200433870eSAlexander V. Chernikov 		    nhop_get_idx(nh_fib));
3210433870eSAlexander V. Chernikov 	}
3220433870eSAlexander V. Chernikov 
3230433870eSAlexander V. Chernikov 	return (false);
3240433870eSAlexander V. Chernikov }
3250433870eSAlexander V. Chernikov 
3260433870eSAlexander V. Chernikov /* Random lookups: correctness verification */
3270433870eSAlexander V. Chernikov static uint64_t
3280433870eSAlexander V. Chernikov run_test_inet_one_pass_random()
3290433870eSAlexander V. Chernikov {
3300433870eSAlexander V. Chernikov 	/* Assume epoch */
3310433870eSAlexander V. Chernikov 	struct in_addr a[64];
3320433870eSAlexander V. Chernikov 	int sz = 64;
3330433870eSAlexander V. Chernikov 	uint64_t count = 0;
3340433870eSAlexander V. Chernikov 
3350433870eSAlexander V. Chernikov 	for (int pass = 0; pass < CHUNK_SIZE / sz; pass++) {
3360433870eSAlexander V. Chernikov 		arc4random_buf(a, sizeof(a));
3370433870eSAlexander V. Chernikov 		for (int i = 0; i < sz; i++) {
3380433870eSAlexander V. Chernikov 			if (!cmp_dst(RT_DEFAULT_FIB, a[i]))
3390433870eSAlexander V. Chernikov 				return (0);
3400433870eSAlexander V. Chernikov 			count++;
3410433870eSAlexander V. Chernikov 		}
3420433870eSAlexander V. Chernikov 	}
3430433870eSAlexander V. Chernikov 	return (count);
3440433870eSAlexander V. Chernikov }
3450433870eSAlexander V. Chernikov 
3460433870eSAlexander V. Chernikov static int
3470433870eSAlexander V. Chernikov run_test_inet_random(SYSCTL_HANDLER_ARGS)
3480433870eSAlexander V. Chernikov {
3490433870eSAlexander V. Chernikov 	struct epoch_tracker et;
3500433870eSAlexander V. Chernikov 
3510433870eSAlexander V. Chernikov 	int count = 0;
3520433870eSAlexander V. Chernikov 	int error = sysctl_handle_int(oidp, &count, 0, req);
3530433870eSAlexander V. Chernikov 	if (error != 0)
3540433870eSAlexander V. Chernikov 		return (error);
3550433870eSAlexander V. Chernikov 
3560433870eSAlexander V. Chernikov 	if (count == 0)
3570433870eSAlexander V. Chernikov 		return (0);
3580433870eSAlexander V. Chernikov 
3590433870eSAlexander V. Chernikov 	if (count < CHUNK_SIZE)
3600433870eSAlexander V. Chernikov 		count = CHUNK_SIZE;
3610433870eSAlexander V. Chernikov 
3620433870eSAlexander V. Chernikov 	struct timespec ts_pre, ts_post;
3630433870eSAlexander V. Chernikov 	int64_t pass_diff, total_diff = 1;
3640433870eSAlexander V. Chernikov 	uint64_t pass_packets, total_packets = 0;
3650433870eSAlexander V. Chernikov 
3660433870eSAlexander V. Chernikov 	for (int pass = 0; pass < count / CHUNK_SIZE; pass++) {
3670433870eSAlexander V. Chernikov 		NET_EPOCH_ENTER(et);
3680433870eSAlexander V. Chernikov 		nanouptime(&ts_pre);
3690433870eSAlexander V. Chernikov 		pass_packets = run_test_inet_one_pass_random();
3700433870eSAlexander V. Chernikov 		nanouptime(&ts_post);
3710433870eSAlexander V. Chernikov 		NET_EPOCH_EXIT(et);
3720433870eSAlexander V. Chernikov 
3730433870eSAlexander V. Chernikov 		pass_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
3740433870eSAlexander V. Chernikov 		    (ts_post.tv_nsec - ts_pre.tv_nsec);
3750433870eSAlexander V. Chernikov 		total_diff += pass_diff;
3760433870eSAlexander V. Chernikov 		total_packets += pass_packets;
3770433870eSAlexander V. Chernikov 
3780433870eSAlexander V. Chernikov 		if (pass_packets == 0)
3790433870eSAlexander V. Chernikov 			break;
3800433870eSAlexander V. Chernikov 	}
3810433870eSAlexander V. Chernikov 
3820433870eSAlexander V. Chernikov 	/* Signal error to userland */
3830433870eSAlexander V. Chernikov 	if (pass_packets == 0)
3840433870eSAlexander V. Chernikov 		return (EINVAL);
3850433870eSAlexander V. Chernikov 
3860433870eSAlexander V. Chernikov 	printf("%zu packets in %zu nanoseconds, %zu pps\n",
3870433870eSAlexander V. Chernikov 	    total_packets, total_diff, total_packets * 1000000000 / total_diff);
3880433870eSAlexander V. Chernikov 
3890433870eSAlexander V. Chernikov 	return (0);
3900433870eSAlexander V. Chernikov }
3910433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet_random,
39274935ce8SAlexander V. Chernikov     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
3930433870eSAlexander V. Chernikov     0, 0, run_test_inet_random, "I", "Execute fib4_lookup random check tests");
3940433870eSAlexander V. Chernikov 
3950433870eSAlexander V. Chernikov 
3960433870eSAlexander V. Chernikov struct inet_array {
3970433870eSAlexander V. Chernikov 	uint32_t alloc_items;
3980433870eSAlexander V. Chernikov 	uint32_t num_items;
3990433870eSAlexander V. Chernikov 	struct in_addr *arr;
4000433870eSAlexander V. Chernikov 	int error;
4010433870eSAlexander V. Chernikov };
4020433870eSAlexander V. Chernikov 
4030433870eSAlexander V. Chernikov /*
4040433870eSAlexander V. Chernikov  * For each prefix, add the following records to the lookup array:
4050433870eSAlexander V. Chernikov  * * prefix-1, prefix, prefix + 1, prefix_end, prefix_end + 1
4060433870eSAlexander V. Chernikov  */
4070433870eSAlexander V. Chernikov static int
4080433870eSAlexander V. Chernikov add_prefix(struct rtentry *rt, void *_data)
4090433870eSAlexander V. Chernikov {
4100433870eSAlexander V. Chernikov 	struct inet_array *pa = (struct inet_array *)_data;
4110433870eSAlexander V. Chernikov 	struct in_addr addr;
4120433870eSAlexander V. Chernikov 	int plen;
4130433870eSAlexander V. Chernikov 	uint32_t scopeid, haddr;
4140433870eSAlexander V. Chernikov 
4150433870eSAlexander V. Chernikov 	if (pa->num_items + 5 >= pa->alloc_items) {
4160433870eSAlexander V. Chernikov 		if (pa->error == 0)
4170433870eSAlexander V. Chernikov 			pa->error = EINVAL;
4180433870eSAlexander V. Chernikov 		return (0);
4190433870eSAlexander V. Chernikov 	}
4200433870eSAlexander V. Chernikov 
4210433870eSAlexander V. Chernikov 	rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid);
4220433870eSAlexander V. Chernikov 
4230433870eSAlexander V. Chernikov 	pa->arr[pa->num_items++] = addr;
4240433870eSAlexander V. Chernikov 	haddr = ntohl(addr.s_addr);
4250433870eSAlexander V. Chernikov 	if (haddr > 0) {
4260433870eSAlexander V. Chernikov 		pa->arr[pa->num_items++].s_addr = htonl(haddr - 1);
4270433870eSAlexander V. Chernikov 		pa->arr[pa->num_items++].s_addr = htonl(haddr + 1);
4280433870eSAlexander V. Chernikov 		/* assume mask != 0 */
4290433870eSAlexander V. Chernikov 		uint32_t mlen = (1 << (32 - plen)) - 1;
4300433870eSAlexander V. Chernikov 		pa->arr[pa->num_items++].s_addr = htonl(haddr + mlen);
4310433870eSAlexander V. Chernikov 		/* can overflow, but who cares */
4320433870eSAlexander V. Chernikov 		pa->arr[pa->num_items++].s_addr = htonl(haddr + mlen + 1);
4330433870eSAlexander V. Chernikov 	}
4340433870eSAlexander V. Chernikov 
4350433870eSAlexander V. Chernikov 	return (0);
4360433870eSAlexander V. Chernikov }
4370433870eSAlexander V. Chernikov 
4380433870eSAlexander V. Chernikov static bool
4390433870eSAlexander V. Chernikov prepare_list(uint32_t fibnum, struct inet_array *pa)
4400433870eSAlexander V. Chernikov {
4410433870eSAlexander V. Chernikov 	struct rib_head *rh;
4420433870eSAlexander V. Chernikov 
4430433870eSAlexander V. Chernikov 	rh = rt_tables_get_rnh(fibnum, AF_INET);
4440433870eSAlexander V. Chernikov 
4450433870eSAlexander V. Chernikov 	uint32_t num_prefixes = (rh->rnh_prefixes + 10) * 5;
4460433870eSAlexander V. Chernikov 	bzero(pa, sizeof(struct inet_array));
4470433870eSAlexander V. Chernikov 	pa->alloc_items = num_prefixes;
4480433870eSAlexander V. Chernikov 	pa->arr = mallocarray(num_prefixes, sizeof(struct in_addr),
4490433870eSAlexander V. Chernikov 	    M_TEMP, M_ZERO | M_WAITOK);
4500433870eSAlexander V. Chernikov 
4510433870eSAlexander V. Chernikov 	rib_walk(RT_DEFAULT_FIB, AF_INET, false, add_prefix, pa);
4520433870eSAlexander V. Chernikov 
4530433870eSAlexander V. Chernikov 	return (pa->error == 0);
4540433870eSAlexander V. Chernikov }
4550433870eSAlexander V. Chernikov 
4560433870eSAlexander V. Chernikov static int
4570433870eSAlexander V. Chernikov run_test_inet_scan(SYSCTL_HANDLER_ARGS)
4580433870eSAlexander V. Chernikov {
4590433870eSAlexander V. Chernikov 	struct epoch_tracker et;
4600433870eSAlexander V. Chernikov 
4610433870eSAlexander V. Chernikov 	int count = 0;
4620433870eSAlexander V. Chernikov 	int error = sysctl_handle_int(oidp, &count, 0, req);
4630433870eSAlexander V. Chernikov 	if (error != 0)
4640433870eSAlexander V. Chernikov 		return (error);
4650433870eSAlexander V. Chernikov 
4660433870eSAlexander V. Chernikov 	if (count == 0)
4670433870eSAlexander V. Chernikov 		return (0);
4680433870eSAlexander V. Chernikov 
4690433870eSAlexander V. Chernikov 	struct inet_array pa = {};
4700433870eSAlexander V. Chernikov 
4710433870eSAlexander V. Chernikov 	if (!prepare_list(RT_DEFAULT_FIB, &pa))
4720433870eSAlexander V. Chernikov 		return (pa.error);
4730433870eSAlexander V. Chernikov 
4740433870eSAlexander V. Chernikov 	struct timespec ts_pre, ts_post;
4750433870eSAlexander V. Chernikov 	int64_t total_diff = 1;
4760433870eSAlexander V. Chernikov 	uint64_t total_packets = 0;
4770433870eSAlexander V. Chernikov 
4780433870eSAlexander V. Chernikov 	NET_EPOCH_ENTER(et);
4790433870eSAlexander V. Chernikov 	nanouptime(&ts_pre);
4800433870eSAlexander V. Chernikov 	for (int i = 0; i < pa.num_items; i++) {
4810433870eSAlexander V. Chernikov 		if (!cmp_dst(RT_DEFAULT_FIB, pa.arr[i])) {
4820433870eSAlexander V. Chernikov 			error = EINVAL;
4830433870eSAlexander V. Chernikov 			break;
4840433870eSAlexander V. Chernikov 		}
4850433870eSAlexander V. Chernikov 		total_packets++;
4860433870eSAlexander V. Chernikov 	}
4870433870eSAlexander V. Chernikov 	nanouptime(&ts_post);
4880433870eSAlexander V. Chernikov 	NET_EPOCH_EXIT(et);
4890433870eSAlexander V. Chernikov 
4900433870eSAlexander V. Chernikov 	if (pa.arr != NULL)
4910433870eSAlexander V. Chernikov 		free(pa.arr, M_TEMP);
4920433870eSAlexander V. Chernikov 
4930433870eSAlexander V. Chernikov 	/* Signal error to userland */
4940433870eSAlexander V. Chernikov 	if (error != 0)
4950433870eSAlexander V. Chernikov 		return (error);
4960433870eSAlexander V. Chernikov 
4970433870eSAlexander V. Chernikov 	total_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
4980433870eSAlexander V. Chernikov 	    (ts_post.tv_nsec - ts_pre.tv_nsec);
4990433870eSAlexander V. Chernikov 	printf("%zu packets in %zu nanoseconds, %zu pps\n",
5000433870eSAlexander V. Chernikov 	    total_packets, total_diff, total_packets * 1000000000 / total_diff);
5010433870eSAlexander V. Chernikov 
5020433870eSAlexander V. Chernikov 	return (0);
5030433870eSAlexander V. Chernikov }
5040433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet_scan,
50574935ce8SAlexander V. Chernikov     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
5060433870eSAlexander V. Chernikov     0, 0, run_test_inet_scan, "I", "Execute fib4_lookup scan tests");
5070433870eSAlexander V. Chernikov 
508*b8598e2fSAlexander V. Chernikov static int
509*b8598e2fSAlexander V. Chernikov rnd_lps(SYSCTL_HANDLER_ARGS)
510*b8598e2fSAlexander V. Chernikov {
511*b8598e2fSAlexander V. Chernikov 	struct epoch_tracker et;
512*b8598e2fSAlexander V. Chernikov 	struct in_addr *keys;
513*b8598e2fSAlexander V. Chernikov 	struct nhop_object **nhops;
514*b8598e2fSAlexander V. Chernikov 
515*b8598e2fSAlexander V. Chernikov 	struct timespec ts_pre, ts_post;
516*b8598e2fSAlexander V. Chernikov 	uint64_t total_diff, lps;
517*b8598e2fSAlexander V. Chernikov 	int error;
518*b8598e2fSAlexander V. Chernikov 	int count = 0;
519*b8598e2fSAlexander V. Chernikov 
520*b8598e2fSAlexander V. Chernikov 	error = sysctl_handle_int(oidp, &count, 0, req);
521*b8598e2fSAlexander V. Chernikov 	if (error != 0)
522*b8598e2fSAlexander V. Chernikov 		return (error);
523*b8598e2fSAlexander V. Chernikov 	if (count <= 0)
524*b8598e2fSAlexander V. Chernikov 		return (0);
525*b8598e2fSAlexander V. Chernikov 
526*b8598e2fSAlexander V. Chernikov 	keys = malloc(sizeof(*keys) * count, M_TEMP, M_NOWAIT);
527*b8598e2fSAlexander V. Chernikov 	nhops = malloc(sizeof(*nhops) * count, M_TEMP, M_NOWAIT);
528*b8598e2fSAlexander V. Chernikov 	if (keys == NULL || nhops == NULL) {
529*b8598e2fSAlexander V. Chernikov 		free(keys, M_TEMP);
530*b8598e2fSAlexander V. Chernikov 		free(nhops, M_TEMP);
531*b8598e2fSAlexander V. Chernikov 		return (ENOMEM);
532*b8598e2fSAlexander V. Chernikov 	}
533*b8598e2fSAlexander V. Chernikov 
534*b8598e2fSAlexander V. Chernikov 	printf("Preparing %d random keys...\n", count);
535*b8598e2fSAlexander V. Chernikov 	arc4random_buf(keys, sizeof(*keys) * count);
536*b8598e2fSAlexander V. Chernikov 	printf("Starting LPS test...\n");
537*b8598e2fSAlexander V. Chernikov 
538*b8598e2fSAlexander V. Chernikov 	NET_EPOCH_ENTER(et);
539*b8598e2fSAlexander V. Chernikov 	nanouptime(&ts_pre);
540*b8598e2fSAlexander V. Chernikov 	for (int i = 0; i < count; i++)
541*b8598e2fSAlexander V. Chernikov 		nhops[i] = fib4_lookup(RT_DEFAULT_FIB, keys[i], 0, NHR_NONE, 0);
542*b8598e2fSAlexander V. Chernikov 	nanouptime(&ts_post);
543*b8598e2fSAlexander V. Chernikov 	NET_EPOCH_EXIT(et);
544*b8598e2fSAlexander V. Chernikov 
545*b8598e2fSAlexander V. Chernikov 	free(keys, M_TEMP);
546*b8598e2fSAlexander V. Chernikov 	free(nhops, M_TEMP);
547*b8598e2fSAlexander V. Chernikov 
548*b8598e2fSAlexander V. Chernikov 	total_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
549*b8598e2fSAlexander V. Chernikov 	    (ts_post.tv_nsec - ts_pre.tv_nsec);
550*b8598e2fSAlexander V. Chernikov 	lps = 1000000000ULL * count / total_diff;
551*b8598e2fSAlexander V. Chernikov 	printf("%d lookups in %zu nanoseconds, %lu.%06lu MLPS\n",
552*b8598e2fSAlexander V. Chernikov 	    count, total_diff, lps / 1000000, lps % 1000000);
553*b8598e2fSAlexander V. Chernikov 
554*b8598e2fSAlexander V. Chernikov 	return (0);
555*b8598e2fSAlexander V. Chernikov }
556*b8598e2fSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, rnd_lps,
557*b8598e2fSAlexander V. Chernikov     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
558*b8598e2fSAlexander V. Chernikov     0, 0, rnd_lps, "I",
559*b8598e2fSAlexander V. Chernikov     "Measure lookups per second using uniformly random keys");
5600433870eSAlexander V. Chernikov 
5610433870eSAlexander V. Chernikov static int
5620433870eSAlexander V. Chernikov test_fib_lookup_modevent(module_t mod, int type, void *unused)
5630433870eSAlexander V. Chernikov {
5640433870eSAlexander V. Chernikov 	int error = 0;
5650433870eSAlexander V. Chernikov 
5660433870eSAlexander V. Chernikov 	switch (type) {
5670433870eSAlexander V. Chernikov 	case MOD_LOAD:
5680433870eSAlexander V. Chernikov 		break;
5690433870eSAlexander V. Chernikov 	case MOD_UNLOAD:
5700433870eSAlexander V. Chernikov 		if (V_inet_addr_list != NULL)
5710433870eSAlexander V. Chernikov 			free(V_inet_addr_list, M_TEMP);
5720433870eSAlexander V. Chernikov 		if (V_inet6_addr_list != NULL)
5730433870eSAlexander V. Chernikov 			free(V_inet6_addr_list, M_TEMP);
5740433870eSAlexander V. Chernikov 		break;
5750433870eSAlexander V. Chernikov 	default:
5760433870eSAlexander V. Chernikov 		error = EOPNOTSUPP;
5770433870eSAlexander V. Chernikov 		break;
5780433870eSAlexander V. Chernikov 	}
5790433870eSAlexander V. Chernikov 	return (error);
5800433870eSAlexander V. Chernikov }
5810433870eSAlexander V. Chernikov 
5820433870eSAlexander V. Chernikov static moduledata_t testfiblookupmod = {
5830433870eSAlexander V. Chernikov         "test_fib_lookup",
5840433870eSAlexander V. Chernikov         test_fib_lookup_modevent,
5850433870eSAlexander V. Chernikov         0
5860433870eSAlexander V. Chernikov };
5870433870eSAlexander V. Chernikov 
5880433870eSAlexander V. Chernikov DECLARE_MODULE(testfiblookupmod, testfiblookupmod, SI_SUB_PSEUDO, SI_ORDER_ANY);
5890433870eSAlexander V. Chernikov MODULE_VERSION(testfiblookup, 1);
590