xref: /freebsd/sys/tests/fib_lookup/fib_lookup.c (revision 4a77a9b6491093b9a8bb786a861ed74ddf156e8e)
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
162*4a77a9b6SAlexander V. Chernikov run_test_inet_one_pass(uint32_t fibnum)
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++) {
172*4a77a9b6SAlexander V. Chernikov 			fib4_lookup(fibnum, 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;
202*4a77a9b6SAlexander V. Chernikov 	uint32_t fibnum = curthread->td_proc->p_fibnum;
2030433870eSAlexander V. Chernikov 
2040433870eSAlexander V. Chernikov 	for (int pass = 0; pass < count / CHUNK_SIZE; pass++) {
2050433870eSAlexander V. Chernikov 		NET_EPOCH_ENTER(et);
2060433870eSAlexander V. Chernikov 		nanouptime(&ts_pre);
207*4a77a9b6SAlexander V. Chernikov 		pass_packets = run_test_inet_one_pass(fibnum);
2080433870eSAlexander V. Chernikov 		nanouptime(&ts_post);
2090433870eSAlexander V. Chernikov 		NET_EPOCH_EXIT(et);
2100433870eSAlexander V. Chernikov 
2110433870eSAlexander V. Chernikov 		pass_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
2120433870eSAlexander V. Chernikov 		    (ts_post.tv_nsec - ts_pre.tv_nsec);
2130433870eSAlexander V. Chernikov 		total_diff += pass_diff;
2140433870eSAlexander V. Chernikov 		total_packets += pass_packets;
2150433870eSAlexander V. Chernikov 	}
2160433870eSAlexander V. Chernikov 
2170433870eSAlexander V. Chernikov 	printf("%zu packets in %zu nanoseconds, %zu pps\n",
2180433870eSAlexander V. Chernikov 	    total_packets, total_diff, total_packets * 1000000000 / total_diff);
2190433870eSAlexander V. Chernikov 
2200433870eSAlexander V. Chernikov 	return (0);
2210433870eSAlexander V. Chernikov }
2220433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet,
22374935ce8SAlexander V. Chernikov     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
2240433870eSAlexander V. Chernikov     0, 0, run_test_inet, "I", "Execute fib4_lookup test");
2250433870eSAlexander V. Chernikov 
2260433870eSAlexander V. Chernikov static uint64_t
227*4a77a9b6SAlexander V. Chernikov run_test_inet6_one_pass(uint32_t fibnum)
2280433870eSAlexander V. Chernikov {
2290433870eSAlexander V. Chernikov 	/* Assume epoch */
2300433870eSAlexander V. Chernikov 	int sz = V_inet6_list_size;
2310433870eSAlexander V. Chernikov 	int tries = CHUNK_SIZE / sz;
2320433870eSAlexander V. Chernikov 	const struct in6_addr *a = V_inet6_addr_list;
2330433870eSAlexander V. Chernikov 	uint64_t count = 0;
2340433870eSAlexander V. Chernikov 
2350433870eSAlexander V. Chernikov 	for (int pass = 0; pass < tries; pass++) {
2360433870eSAlexander V. Chernikov 		for (int i = 0; i < sz; i++) {
237*4a77a9b6SAlexander V. Chernikov 			fib6_lookup(fibnum, &a[i], 0, NHR_NONE, 0);
2380433870eSAlexander V. Chernikov 			count++;
2390433870eSAlexander V. Chernikov 		}
2400433870eSAlexander V. Chernikov 	}
2410433870eSAlexander V. Chernikov 	return (count);
2420433870eSAlexander V. Chernikov }
2430433870eSAlexander V. Chernikov 
2440433870eSAlexander V. Chernikov static int
2450433870eSAlexander V. Chernikov run_test_inet6(SYSCTL_HANDLER_ARGS)
2460433870eSAlexander V. Chernikov {
2470433870eSAlexander V. Chernikov 	struct epoch_tracker et;
2480433870eSAlexander V. Chernikov 
2490433870eSAlexander V. Chernikov 	int count = 0;
2500433870eSAlexander V. Chernikov 	int error = sysctl_handle_int(oidp, &count, 0, req);
2510433870eSAlexander V. Chernikov 	if (error != 0)
2520433870eSAlexander V. Chernikov 		return (error);
2530433870eSAlexander V. Chernikov 
2540433870eSAlexander V. Chernikov 	if (count == 0)
2550433870eSAlexander V. Chernikov 		return (0);
2560433870eSAlexander V. Chernikov 
2570433870eSAlexander V. Chernikov 	if (V_inet6_list_size <= 0)
2580433870eSAlexander V. Chernikov 		return (ENOENT);
2590433870eSAlexander V. Chernikov 
2600433870eSAlexander V. Chernikov 	printf("run: %d packets vnet %p\n", count, curvnet);
2610433870eSAlexander V. Chernikov 	if (count < CHUNK_SIZE)
2620433870eSAlexander V. Chernikov 		count = CHUNK_SIZE;
2630433870eSAlexander V. Chernikov 
2640433870eSAlexander V. Chernikov 	struct timespec ts_pre, ts_post;
2650433870eSAlexander V. Chernikov 	int64_t pass_diff, total_diff = 0;
2660433870eSAlexander V. Chernikov 	uint64_t pass_packets, total_packets = 0;
267*4a77a9b6SAlexander V. Chernikov 	uint32_t fibnum = curthread->td_proc->p_fibnum;
2680433870eSAlexander V. Chernikov 
2690433870eSAlexander V. Chernikov 	for (int pass = 0; pass < count / CHUNK_SIZE; pass++) {
2700433870eSAlexander V. Chernikov 		NET_EPOCH_ENTER(et);
2710433870eSAlexander V. Chernikov 		nanouptime(&ts_pre);
272*4a77a9b6SAlexander V. Chernikov 		pass_packets = run_test_inet6_one_pass(fibnum);
2730433870eSAlexander V. Chernikov 		nanouptime(&ts_post);
2740433870eSAlexander V. Chernikov 		NET_EPOCH_EXIT(et);
2750433870eSAlexander V. Chernikov 
2760433870eSAlexander V. Chernikov 		pass_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
2770433870eSAlexander V. Chernikov 		    (ts_post.tv_nsec - ts_pre.tv_nsec);
2780433870eSAlexander V. Chernikov 		total_diff += pass_diff;
2790433870eSAlexander V. Chernikov 		total_packets += pass_packets;
2800433870eSAlexander V. Chernikov 	}
2810433870eSAlexander V. Chernikov 
2820433870eSAlexander V. Chernikov 	printf("%zu packets in %zu nanoseconds, %zu pps\n",
2830433870eSAlexander V. Chernikov 	    total_packets, total_diff, total_packets * 1000000000 / total_diff);
2840433870eSAlexander V. Chernikov 
2850433870eSAlexander V. Chernikov 	return (0);
2860433870eSAlexander V. Chernikov }
2870433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet6,
28874935ce8SAlexander V. Chernikov     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
2890433870eSAlexander V. Chernikov     0, 0, run_test_inet6, "I", "Execute fib6_lookup test");
2900433870eSAlexander V. Chernikov 
2910433870eSAlexander V. Chernikov static bool
2920433870eSAlexander V. Chernikov cmp_dst(uint32_t fibnum, struct in_addr a)
2930433870eSAlexander V. Chernikov {
2940433870eSAlexander V. Chernikov 	struct nhop_object *nh_fib;
2950433870eSAlexander V. Chernikov 	struct rtentry *rt;
2960433870eSAlexander V. Chernikov 	struct route_nhop_data rnd = {};
2970433870eSAlexander V. Chernikov 
2980433870eSAlexander V. Chernikov 	nh_fib = fib4_lookup(fibnum, a, 0, NHR_NONE, 0);
2990433870eSAlexander V. Chernikov 	rt = fib4_lookup_rt(fibnum, a, 0, NHR_NONE, &rnd);
3000433870eSAlexander V. Chernikov 
3010433870eSAlexander V. Chernikov 	if (nh_fib == NULL && rt == NULL) {
3020433870eSAlexander V. Chernikov 		return (true);
3030433870eSAlexander V. Chernikov 	} else if (nh_fib == nhop_select(rnd.rnd_nhop, 0)) {
3040433870eSAlexander V. Chernikov 		return (true);
3050433870eSAlexander V. Chernikov 	}
3060433870eSAlexander V. Chernikov 
3070433870eSAlexander V. Chernikov 	struct in_addr dst;
3080433870eSAlexander V. Chernikov 	int plen;
3090433870eSAlexander V. Chernikov 	uint32_t scopeid;
3100433870eSAlexander V. Chernikov 	char key_str[INET_ADDRSTRLEN], dst_str[INET_ADDRSTRLEN];
3110433870eSAlexander V. Chernikov 
3120433870eSAlexander V. Chernikov 	inet_ntop(AF_INET, &a, key_str, sizeof(key_str));
3130433870eSAlexander V. Chernikov 	if (rnd.rnd_nhop == NULL) {
3140433870eSAlexander V. Chernikov 		printf("[RT BUG] lookup for %s: RIB: ENOENT FIB: nh=%u\n",
3150433870eSAlexander V. Chernikov 		    key_str, nhop_get_idx(nh_fib));
3160433870eSAlexander V. Chernikov 	} else {
3170433870eSAlexander V. Chernikov 		rt_get_inet_prefix_plen(rt, &dst, &plen, &scopeid);
3180433870eSAlexander V. Chernikov 		inet_ntop(AF_INET, &dst, dst_str, sizeof(dst_str));
3190433870eSAlexander V. Chernikov 		printf("[RT BUG] lookup for %s: RIB: %s/%d,nh=%u FIB: nh=%u\n",
3200433870eSAlexander V. Chernikov 		    key_str, dst_str, plen,
3210433870eSAlexander V. Chernikov 		    nhop_get_idx(nhop_select(rnd.rnd_nhop, 0)),
3220433870eSAlexander V. Chernikov 		    nhop_get_idx(nh_fib));
3230433870eSAlexander V. Chernikov 	}
3240433870eSAlexander V. Chernikov 
3250433870eSAlexander V. Chernikov 	return (false);
3260433870eSAlexander V. Chernikov }
3270433870eSAlexander V. Chernikov 
3280433870eSAlexander V. Chernikov /* Random lookups: correctness verification */
3290433870eSAlexander V. Chernikov static uint64_t
330*4a77a9b6SAlexander V. Chernikov run_test_inet_one_pass_random(uint32_t fibnum)
3310433870eSAlexander V. Chernikov {
3320433870eSAlexander V. Chernikov 	/* Assume epoch */
3330433870eSAlexander V. Chernikov 	struct in_addr a[64];
3340433870eSAlexander V. Chernikov 	int sz = 64;
3350433870eSAlexander V. Chernikov 	uint64_t count = 0;
3360433870eSAlexander V. Chernikov 
3370433870eSAlexander V. Chernikov 	for (int pass = 0; pass < CHUNK_SIZE / sz; pass++) {
3380433870eSAlexander V. Chernikov 		arc4random_buf(a, sizeof(a));
3390433870eSAlexander V. Chernikov 		for (int i = 0; i < sz; i++) {
340*4a77a9b6SAlexander V. Chernikov 			if (!cmp_dst(fibnum, a[i]))
3410433870eSAlexander V. Chernikov 				return (0);
3420433870eSAlexander V. Chernikov 			count++;
3430433870eSAlexander V. Chernikov 		}
3440433870eSAlexander V. Chernikov 	}
3450433870eSAlexander V. Chernikov 	return (count);
3460433870eSAlexander V. Chernikov }
3470433870eSAlexander V. Chernikov 
3480433870eSAlexander V. Chernikov static int
3490433870eSAlexander V. Chernikov run_test_inet_random(SYSCTL_HANDLER_ARGS)
3500433870eSAlexander V. Chernikov {
3510433870eSAlexander V. Chernikov 	struct epoch_tracker et;
3520433870eSAlexander V. Chernikov 
3530433870eSAlexander V. Chernikov 	int count = 0;
3540433870eSAlexander V. Chernikov 	int error = sysctl_handle_int(oidp, &count, 0, req);
3550433870eSAlexander V. Chernikov 	if (error != 0)
3560433870eSAlexander V. Chernikov 		return (error);
3570433870eSAlexander V. Chernikov 
3580433870eSAlexander V. Chernikov 	if (count == 0)
3590433870eSAlexander V. Chernikov 		return (0);
3600433870eSAlexander V. Chernikov 
3610433870eSAlexander V. Chernikov 	if (count < CHUNK_SIZE)
3620433870eSAlexander V. Chernikov 		count = CHUNK_SIZE;
3630433870eSAlexander V. Chernikov 
3640433870eSAlexander V. Chernikov 	struct timespec ts_pre, ts_post;
3650433870eSAlexander V. Chernikov 	int64_t pass_diff, total_diff = 1;
3660433870eSAlexander V. Chernikov 	uint64_t pass_packets, total_packets = 0;
367*4a77a9b6SAlexander V. Chernikov 	uint32_t fibnum = curthread->td_proc->p_fibnum;
3680433870eSAlexander V. Chernikov 
3690433870eSAlexander V. Chernikov 	for (int pass = 0; pass < count / CHUNK_SIZE; pass++) {
3700433870eSAlexander V. Chernikov 		NET_EPOCH_ENTER(et);
3710433870eSAlexander V. Chernikov 		nanouptime(&ts_pre);
372*4a77a9b6SAlexander V. Chernikov 		pass_packets = run_test_inet_one_pass_random(fibnum);
3730433870eSAlexander V. Chernikov 		nanouptime(&ts_post);
3740433870eSAlexander V. Chernikov 		NET_EPOCH_EXIT(et);
3750433870eSAlexander V. Chernikov 
3760433870eSAlexander V. Chernikov 		pass_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
3770433870eSAlexander V. Chernikov 		    (ts_post.tv_nsec - ts_pre.tv_nsec);
3780433870eSAlexander V. Chernikov 		total_diff += pass_diff;
3790433870eSAlexander V. Chernikov 		total_packets += pass_packets;
3800433870eSAlexander V. Chernikov 
3810433870eSAlexander V. Chernikov 		if (pass_packets == 0)
3820433870eSAlexander V. Chernikov 			break;
3830433870eSAlexander V. Chernikov 	}
3840433870eSAlexander V. Chernikov 
3850433870eSAlexander V. Chernikov 	/* Signal error to userland */
3860433870eSAlexander V. Chernikov 	if (pass_packets == 0)
3870433870eSAlexander V. Chernikov 		return (EINVAL);
3880433870eSAlexander V. Chernikov 
3890433870eSAlexander V. Chernikov 	printf("%zu packets in %zu nanoseconds, %zu pps\n",
3900433870eSAlexander V. Chernikov 	    total_packets, total_diff, total_packets * 1000000000 / total_diff);
3910433870eSAlexander V. Chernikov 
3920433870eSAlexander V. Chernikov 	return (0);
3930433870eSAlexander V. Chernikov }
3940433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet_random,
39574935ce8SAlexander V. Chernikov     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
3960433870eSAlexander V. Chernikov     0, 0, run_test_inet_random, "I", "Execute fib4_lookup random check tests");
3970433870eSAlexander V. Chernikov 
3980433870eSAlexander V. Chernikov 
3990433870eSAlexander V. Chernikov struct inet_array {
4000433870eSAlexander V. Chernikov 	uint32_t alloc_items;
4010433870eSAlexander V. Chernikov 	uint32_t num_items;
402*4a77a9b6SAlexander V. Chernikov 	uint32_t rnh_prefixes;
4030433870eSAlexander V. Chernikov 	int error;
404*4a77a9b6SAlexander V. Chernikov 	struct in_addr *arr;
4050433870eSAlexander V. Chernikov };
4060433870eSAlexander V. Chernikov 
4070433870eSAlexander V. Chernikov /*
4080433870eSAlexander V. Chernikov  * For each prefix, add the following records to the lookup array:
4090433870eSAlexander V. Chernikov  * * prefix-1, prefix, prefix + 1, prefix_end, prefix_end + 1
4100433870eSAlexander V. Chernikov  */
4110433870eSAlexander V. Chernikov static int
4120433870eSAlexander V. Chernikov add_prefix(struct rtentry *rt, void *_data)
4130433870eSAlexander V. Chernikov {
4140433870eSAlexander V. Chernikov 	struct inet_array *pa = (struct inet_array *)_data;
4150433870eSAlexander V. Chernikov 	struct in_addr addr;
4160433870eSAlexander V. Chernikov 	int plen;
4170433870eSAlexander V. Chernikov 	uint32_t scopeid, haddr;
4180433870eSAlexander V. Chernikov 
419*4a77a9b6SAlexander V. Chernikov 	pa->rnh_prefixes++;
420*4a77a9b6SAlexander V. Chernikov 
4210433870eSAlexander V. Chernikov 	if (pa->num_items + 5 >= pa->alloc_items) {
4220433870eSAlexander V. Chernikov 		if (pa->error == 0)
423*4a77a9b6SAlexander V. Chernikov 			pa->error = ENOSPC;
4240433870eSAlexander V. Chernikov 		return (0);
4250433870eSAlexander V. Chernikov 	}
4260433870eSAlexander V. Chernikov 
4270433870eSAlexander V. Chernikov 	rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid);
4280433870eSAlexander V. Chernikov 
4290433870eSAlexander V. Chernikov 	pa->arr[pa->num_items++] = addr;
4300433870eSAlexander V. Chernikov 	haddr = ntohl(addr.s_addr);
4310433870eSAlexander V. Chernikov 	if (haddr > 0) {
4320433870eSAlexander V. Chernikov 		pa->arr[pa->num_items++].s_addr = htonl(haddr - 1);
4330433870eSAlexander V. Chernikov 		pa->arr[pa->num_items++].s_addr = htonl(haddr + 1);
4340433870eSAlexander V. Chernikov 		/* assume mask != 0 */
4350433870eSAlexander V. Chernikov 		uint32_t mlen = (1 << (32 - plen)) - 1;
4360433870eSAlexander V. Chernikov 		pa->arr[pa->num_items++].s_addr = htonl(haddr + mlen);
4370433870eSAlexander V. Chernikov 		/* can overflow, but who cares */
4380433870eSAlexander V. Chernikov 		pa->arr[pa->num_items++].s_addr = htonl(haddr + mlen + 1);
4390433870eSAlexander V. Chernikov 	}
4400433870eSAlexander V. Chernikov 
4410433870eSAlexander V. Chernikov 	return (0);
4420433870eSAlexander V. Chernikov }
4430433870eSAlexander V. Chernikov 
4440433870eSAlexander V. Chernikov static bool
4450433870eSAlexander V. Chernikov prepare_list(uint32_t fibnum, struct inet_array *pa)
4460433870eSAlexander V. Chernikov {
4470433870eSAlexander V. Chernikov 	struct rib_head *rh;
4480433870eSAlexander V. Chernikov 
4490433870eSAlexander V. Chernikov 	rh = rt_tables_get_rnh(fibnum, AF_INET);
4500433870eSAlexander V. Chernikov 
451*4a77a9b6SAlexander V. Chernikov 	uint32_t num_prefixes = rh->rnh_prefixes;
4520433870eSAlexander V. Chernikov 	bzero(pa, sizeof(struct inet_array));
453*4a77a9b6SAlexander V. Chernikov 	pa->alloc_items = (num_prefixes + 10) * 5;
454*4a77a9b6SAlexander V. Chernikov 	pa->arr = mallocarray(pa->alloc_items, sizeof(struct in_addr),
4550433870eSAlexander V. Chernikov 	    M_TEMP, M_ZERO | M_WAITOK);
4560433870eSAlexander V. Chernikov 
457*4a77a9b6SAlexander V. Chernikov 	rib_walk(fibnum, AF_INET, false, add_prefix, pa);
458*4a77a9b6SAlexander V. Chernikov 
459*4a77a9b6SAlexander V. Chernikov 	if (pa->error != 0) {
460*4a77a9b6SAlexander V. Chernikov 		printf("prefixes: old: %u, current: %u, walked: %u, allocated: %u\n",
461*4a77a9b6SAlexander V. Chernikov 		    num_prefixes, rh->rnh_prefixes, pa->rnh_prefixes, pa->alloc_items);
462*4a77a9b6SAlexander V. Chernikov 	}
4630433870eSAlexander V. Chernikov 
4640433870eSAlexander V. Chernikov 	return (pa->error == 0);
4650433870eSAlexander V. Chernikov }
4660433870eSAlexander V. Chernikov 
4670433870eSAlexander V. Chernikov static int
4680433870eSAlexander V. Chernikov run_test_inet_scan(SYSCTL_HANDLER_ARGS)
4690433870eSAlexander V. Chernikov {
4700433870eSAlexander V. Chernikov 	struct epoch_tracker et;
4710433870eSAlexander V. Chernikov 
4720433870eSAlexander V. Chernikov 	int count = 0;
4730433870eSAlexander V. Chernikov 	int error = sysctl_handle_int(oidp, &count, 0, req);
4740433870eSAlexander V. Chernikov 	if (error != 0)
4750433870eSAlexander V. Chernikov 		return (error);
4760433870eSAlexander V. Chernikov 
4770433870eSAlexander V. Chernikov 	if (count == 0)
4780433870eSAlexander V. Chernikov 		return (0);
4790433870eSAlexander V. Chernikov 
4800433870eSAlexander V. Chernikov 	struct inet_array pa = {};
481*4a77a9b6SAlexander V. Chernikov 	uint32_t fibnum = curthread->td_proc->p_fibnum;
4820433870eSAlexander V. Chernikov 
483*4a77a9b6SAlexander V. Chernikov 	if (!prepare_list(fibnum, &pa))
4840433870eSAlexander V. Chernikov 		return (pa.error);
4850433870eSAlexander V. Chernikov 
4860433870eSAlexander V. Chernikov 	struct timespec ts_pre, ts_post;
4870433870eSAlexander V. Chernikov 	int64_t total_diff = 1;
4880433870eSAlexander V. Chernikov 	uint64_t total_packets = 0;
489*4a77a9b6SAlexander V. Chernikov 	int failure_count = 0;
4900433870eSAlexander V. Chernikov 
4910433870eSAlexander V. Chernikov 	NET_EPOCH_ENTER(et);
4920433870eSAlexander V. Chernikov 	nanouptime(&ts_pre);
4930433870eSAlexander V. Chernikov 	for (int i = 0; i < pa.num_items; i++) {
494*4a77a9b6SAlexander V. Chernikov 		if (!cmp_dst(fibnum, pa.arr[i])) {
495*4a77a9b6SAlexander V. Chernikov 			failure_count++;
4960433870eSAlexander V. Chernikov 		}
4970433870eSAlexander V. Chernikov 		total_packets++;
4980433870eSAlexander V. Chernikov 	}
4990433870eSAlexander V. Chernikov 	nanouptime(&ts_post);
5000433870eSAlexander V. Chernikov 	NET_EPOCH_EXIT(et);
5010433870eSAlexander V. Chernikov 
5020433870eSAlexander V. Chernikov 	if (pa.arr != NULL)
5030433870eSAlexander V. Chernikov 		free(pa.arr, M_TEMP);
5040433870eSAlexander V. Chernikov 
5050433870eSAlexander V. Chernikov 	/* Signal error to userland */
506*4a77a9b6SAlexander V. Chernikov 	if (failure_count > 0) {
507*4a77a9b6SAlexander V. Chernikov 		printf("[RT ERROR] total failures: %d\n", failure_count);
508*4a77a9b6SAlexander V. Chernikov 		return (EINVAL);
509*4a77a9b6SAlexander V. Chernikov 	}
5100433870eSAlexander V. Chernikov 
5110433870eSAlexander V. Chernikov 	total_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
5120433870eSAlexander V. Chernikov 	    (ts_post.tv_nsec - ts_pre.tv_nsec);
5130433870eSAlexander V. Chernikov 	printf("%zu packets in %zu nanoseconds, %zu pps\n",
5140433870eSAlexander V. Chernikov 	    total_packets, total_diff, total_packets * 1000000000 / total_diff);
5150433870eSAlexander V. Chernikov 
5160433870eSAlexander V. Chernikov 	return (0);
5170433870eSAlexander V. Chernikov }
5180433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet_scan,
51974935ce8SAlexander V. Chernikov     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
5200433870eSAlexander V. Chernikov     0, 0, run_test_inet_scan, "I", "Execute fib4_lookup scan tests");
5210433870eSAlexander V. Chernikov 
522b6f8436bSMarko Zec #define	LPS_SEQ		0x1
523b6f8436bSMarko Zec #define	LPS_ANN		0x2
524b6f8436bSMarko Zec #define	LPS_REP		0x4
525b6f8436bSMarko Zec 
526b6f8436bSMarko Zec struct lps_walk_state {
527b6f8436bSMarko Zec 	uint32_t *keys;
528b6f8436bSMarko Zec 	int pos;
529b6f8436bSMarko Zec 	int lim;
530b6f8436bSMarko Zec };
531b6f8436bSMarko Zec 
532b6f8436bSMarko Zec static int
533b6f8436bSMarko Zec reduce_keys(struct rtentry *rt, void *_data)
534b6f8436bSMarko Zec {
535b6f8436bSMarko Zec         struct lps_walk_state *wa = (struct lps_walk_state *) _data;
536b6f8436bSMarko Zec 	struct in_addr addr;
537b6f8436bSMarko Zec 	uint32_t scopeid;
538b6f8436bSMarko Zec 	int plen;
539b6f8436bSMarko Zec 
540b6f8436bSMarko Zec 	rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid);
541b6f8436bSMarko Zec 	wa->keys[wa->pos] = ntohl(addr.s_addr) |
542b6f8436bSMarko Zec 	    (wa->keys[wa->pos] & ~(0xffffffffU << (32 - plen)));
543b6f8436bSMarko Zec 
544b6f8436bSMarko Zec 	wa->pos++;
545b6f8436bSMarko Zec 	return (wa->pos == wa->lim);
546b6f8436bSMarko Zec }
547b6f8436bSMarko Zec 
548b8598e2fSAlexander V. Chernikov static int
549b8598e2fSAlexander V. Chernikov rnd_lps(SYSCTL_HANDLER_ARGS)
550b8598e2fSAlexander V. Chernikov {
551b8598e2fSAlexander V. Chernikov 	struct epoch_tracker et;
552a43104ebSMarko Zec 	struct in_addr key;
553b6f8436bSMarko Zec 	struct lps_walk_state wa;
554b8598e2fSAlexander V. Chernikov 	struct timespec ts_pre, ts_post;
555b6f8436bSMarko Zec 	struct nhop_object *nh_fib;
556b8598e2fSAlexander V. Chernikov 	uint64_t total_diff, lps;
557*4a77a9b6SAlexander V. Chernikov 	uint32_t *keys, fibnum;
558b6f8436bSMarko Zec 	uint32_t t, p;
559a43104ebSMarko Zec 	uintptr_t acc = 0;
560b6f8436bSMarko Zec 	int i, pos, count = 0;
561b6f8436bSMarko Zec 	int seq = 0, rep = 0;
562a43104ebSMarko Zec 	int error;
563b8598e2fSAlexander V. Chernikov 
564b8598e2fSAlexander V. Chernikov 	error = sysctl_handle_int(oidp, &count, 0, req);
565b8598e2fSAlexander V. Chernikov 	if (error != 0)
566b8598e2fSAlexander V. Chernikov 		return (error);
567b8598e2fSAlexander V. Chernikov 	if (count <= 0)
568b8598e2fSAlexander V. Chernikov 		return (0);
569*4a77a9b6SAlexander V. Chernikov 	fibnum = curthread->td_proc->p_fibnum;
570b8598e2fSAlexander V. Chernikov 
571b8598e2fSAlexander V. Chernikov 	keys = malloc(sizeof(*keys) * count, M_TEMP, M_NOWAIT);
572a43104ebSMarko Zec 	if (keys == NULL)
573b8598e2fSAlexander V. Chernikov 		return (ENOMEM);
574b8598e2fSAlexander V. Chernikov 	printf("Preparing %d random keys...\n", count);
575b8598e2fSAlexander V. Chernikov 	arc4random_buf(keys, sizeof(*keys) * count);
576b6f8436bSMarko Zec 	if (arg2 & LPS_ANN) {
577b6f8436bSMarko Zec 		wa.keys = keys;
578b6f8436bSMarko Zec 		wa.pos = 0;
579b6f8436bSMarko Zec 		wa.lim = count;
580b6f8436bSMarko Zec 		printf("Reducing keys to announced address space...\n");
581b6f8436bSMarko Zec 		do {
582*4a77a9b6SAlexander V. Chernikov 			rib_walk(fibnum, AF_INET, false, reduce_keys,
583b6f8436bSMarko Zec 			    &wa);
584b6f8436bSMarko Zec 		} while (wa.pos < wa.lim);
585b6f8436bSMarko Zec 		printf("Reshuffling keys...\n");
586b6f8436bSMarko Zec 		for (int i = 0; i < count; i++) {
587b6f8436bSMarko Zec 			p = random() % count;
588b6f8436bSMarko Zec 			t = keys[p];
589b6f8436bSMarko Zec 			keys[p] = keys[i];
590b6f8436bSMarko Zec 			keys[i] = t;
591b6f8436bSMarko Zec 		}
592b6f8436bSMarko Zec 	}
593b6f8436bSMarko Zec 
594b6f8436bSMarko Zec 	if (arg2 & LPS_REP) {
595b6f8436bSMarko Zec 		rep = 1;
596b6f8436bSMarko Zec 		printf("REP ");
597b6f8436bSMarko Zec 	}
598b6f8436bSMarko Zec 	if (arg2 & LPS_SEQ) {
599b6f8436bSMarko Zec 		seq = 1;
600b6f8436bSMarko Zec 		printf("SEQ");
601b6f8436bSMarko Zec 	} else if (arg2 & LPS_ANN)
602b6f8436bSMarko Zec 		printf("ANN");
603b6f8436bSMarko Zec 	else
604b6f8436bSMarko Zec 		printf("RND");
605b6f8436bSMarko Zec 	printf(" LPS test starting...\n");
606b8598e2fSAlexander V. Chernikov 
607b8598e2fSAlexander V. Chernikov 	NET_EPOCH_ENTER(et);
608b8598e2fSAlexander V. Chernikov 	nanouptime(&ts_pre);
609b6f8436bSMarko Zec 	for (i = 0, pos = 0; i < count; i++) {
610b6f8436bSMarko Zec 		key.s_addr = keys[pos++] ^ ((acc >> 10) & 0xff);
611*4a77a9b6SAlexander V. Chernikov 		nh_fib = fib4_lookup(fibnum, key, 0, NHR_NONE, 0);
612b6f8436bSMarko Zec 		if (seq) {
613b6f8436bSMarko Zec 			if (nh_fib != NULL) {
614b6f8436bSMarko Zec 				acc += (uintptr_t) nh_fib + 123;
615b6f8436bSMarko Zec 				if (acc & 0x1000)
616b6f8436bSMarko Zec 					acc += (uintptr_t) nh_fib->nh_ifp;
617b6f8436bSMarko Zec 				else
618b6f8436bSMarko Zec 					acc -= (uintptr_t) nh_fib->nh_ifp;
619b6f8436bSMarko Zec 			} else
620b6f8436bSMarko Zec 				acc ^= (acc >> 3) + (acc << 2) + i;
621b6f8436bSMarko Zec 			if (acc & 0x800)
622b6f8436bSMarko Zec 				pos++;
623b6f8436bSMarko Zec 			if (pos >= count)
624b6f8436bSMarko Zec 				pos = 0;
625a43104ebSMarko Zec 		}
626b6f8436bSMarko Zec 		if (rep && ((i & 0xf) == 0xf)) {
627b6f8436bSMarko Zec 			pos -= 0xf;
628b6f8436bSMarko Zec 			if (pos < 0)
629b6f8436bSMarko Zec 				pos += 0xf;
630a43104ebSMarko Zec 		}
631a43104ebSMarko Zec 	}
632b8598e2fSAlexander V. Chernikov 	nanouptime(&ts_post);
633b8598e2fSAlexander V. Chernikov 	NET_EPOCH_EXIT(et);
634b8598e2fSAlexander V. Chernikov 
635b8598e2fSAlexander V. Chernikov 	free(keys, M_TEMP);
636b8598e2fSAlexander V. Chernikov 
637b8598e2fSAlexander V. Chernikov 	total_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
638b6f8436bSMarko Zec 	    (ts_post.tv_nsec - ts_pre.tv_nsec);
639b8598e2fSAlexander V. Chernikov 	lps = 1000000000ULL * count / total_diff;
640b6f8436bSMarko Zec 	printf("%d lookups in %zu.%06zu milliseconds, %lu.%06lu MLPS\n",
641b6f8436bSMarko Zec 	    count, total_diff / 1000000, total_diff % 1000000,
642b6f8436bSMarko Zec 	    lps / 1000000, lps % 1000000);
643b8598e2fSAlexander V. Chernikov 
644b8598e2fSAlexander V. Chernikov 	return (0);
645b8598e2fSAlexander V. Chernikov }
646a43104ebSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_rnd,
647a43104ebSMarko Zec     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
648b6f8436bSMarko Zec     0, 0, rnd_lps, "I",
649a43104ebSMarko Zec     "Measure lookups per second, uniformly random keys, independent lookups");
650b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_rnd_ann,
651b6f8436bSMarko Zec     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
652b6f8436bSMarko Zec     0, LPS_ANN, rnd_lps, "I",
653b6f8436bSMarko Zec     "Measure lookups per second, random keys from announced address space, "
654b6f8436bSMarko Zec     "independent lookups");
655b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_seq,
656b6f8436bSMarko Zec     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
657b6f8436bSMarko Zec     0, LPS_SEQ, rnd_lps, "I",
658b6f8436bSMarko Zec     "Measure lookups per second, uniformly random keys, "
659b6f8436bSMarko Zec     "artificial dependencies between lookups");
660b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_seq_ann,
661b6f8436bSMarko Zec     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
662b6f8436bSMarko Zec     0, LPS_SEQ | LPS_ANN, rnd_lps, "I",
663b6f8436bSMarko Zec     "Measure lookups per second, random keys from announced address space, "
664b6f8436bSMarko Zec     "artificial dependencies between lookups");
665b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_rnd_rep,
666b6f8436bSMarko Zec     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
667b6f8436bSMarko Zec     0, LPS_REP, rnd_lps, "I",
668b6f8436bSMarko Zec     "Measure lookups per second, uniformly random keys, independent lookups, "
669b6f8436bSMarko Zec     "repeated keys");
670b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_rnd_ann_rep,
671b6f8436bSMarko Zec     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
672b6f8436bSMarko Zec     0, LPS_ANN | LPS_REP, rnd_lps, "I",
673b6f8436bSMarko Zec     "Measure lookups per second, random keys from announced address space, "
674b6f8436bSMarko Zec     "independent lookups, repeated keys");
675b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_seq_rep,
676b6f8436bSMarko Zec     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
677b6f8436bSMarko Zec     0, LPS_SEQ | LPS_REP, rnd_lps, "I",
678b6f8436bSMarko Zec     "Measure lookups per second, uniformly random keys, "
679b6f8436bSMarko Zec     "artificial dependencies between lookups, repeated keys");
680b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_seq_ann_rep,
681b6f8436bSMarko Zec     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
682b6f8436bSMarko Zec     0, LPS_SEQ | LPS_ANN | LPS_REP, rnd_lps, "I",
683b6f8436bSMarko Zec     "Measure lookups per second, random keys from announced address space, "
684b6f8436bSMarko Zec     "artificial dependencies between lookups, repeated keys");
6850433870eSAlexander V. Chernikov 
6860433870eSAlexander V. Chernikov static int
6870433870eSAlexander V. Chernikov test_fib_lookup_modevent(module_t mod, int type, void *unused)
6880433870eSAlexander V. Chernikov {
6890433870eSAlexander V. Chernikov 	int error = 0;
6900433870eSAlexander V. Chernikov 
6910433870eSAlexander V. Chernikov 	switch (type) {
6920433870eSAlexander V. Chernikov 	case MOD_LOAD:
6930433870eSAlexander V. Chernikov 		break;
6940433870eSAlexander V. Chernikov 	case MOD_UNLOAD:
6950433870eSAlexander V. Chernikov 		if (V_inet_addr_list != NULL)
6960433870eSAlexander V. Chernikov 			free(V_inet_addr_list, M_TEMP);
6970433870eSAlexander V. Chernikov 		if (V_inet6_addr_list != NULL)
6980433870eSAlexander V. Chernikov 			free(V_inet6_addr_list, M_TEMP);
6990433870eSAlexander V. Chernikov 		break;
7000433870eSAlexander V. Chernikov 	default:
7010433870eSAlexander V. Chernikov 		error = EOPNOTSUPP;
7020433870eSAlexander V. Chernikov 		break;
7030433870eSAlexander V. Chernikov 	}
7040433870eSAlexander V. Chernikov 	return (error);
7050433870eSAlexander V. Chernikov }
7060433870eSAlexander V. Chernikov 
7070433870eSAlexander V. Chernikov static moduledata_t testfiblookupmod = {
7080433870eSAlexander V. Chernikov         "test_fib_lookup",
7090433870eSAlexander V. Chernikov         test_fib_lookup_modevent,
7100433870eSAlexander V. Chernikov         0
7110433870eSAlexander V. Chernikov };
7120433870eSAlexander V. Chernikov 
7130433870eSAlexander V. Chernikov DECLARE_MODULE(testfiblookupmod, testfiblookupmod, SI_SUB_PSEUDO, SI_ORDER_ANY);
7140433870eSAlexander V. Chernikov MODULE_VERSION(testfiblookup, 1);
715