10433870eSAlexander V. Chernikov /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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 #include "opt_inet.h"
300433870eSAlexander V. Chernikov #include "opt_inet6.h"
310433870eSAlexander V. Chernikov
320433870eSAlexander V. Chernikov #include <sys/param.h>
330433870eSAlexander V. Chernikov #include <sys/kernel.h>
340433870eSAlexander V. Chernikov #include <sys/lock.h>
350433870eSAlexander V. Chernikov #include <sys/rmlock.h>
360433870eSAlexander V. Chernikov #include <sys/malloc.h>
370433870eSAlexander V. Chernikov #include <sys/module.h>
380433870eSAlexander V. Chernikov #include <sys/kernel.h>
390433870eSAlexander V. Chernikov #include <sys/socket.h>
400433870eSAlexander V. Chernikov #include <sys/sysctl.h>
410433870eSAlexander V. Chernikov #include <net/vnet.h>
420433870eSAlexander V. Chernikov
430433870eSAlexander V. Chernikov #include <net/if.h>
440433870eSAlexander V. Chernikov #include <net/if_var.h>
450433870eSAlexander V. Chernikov
460433870eSAlexander V. Chernikov #include <netinet/in.h>
470433870eSAlexander V. Chernikov #include <netinet/in_fib.h>
480433870eSAlexander V. Chernikov #include <netinet/ip.h>
490433870eSAlexander V. Chernikov
500433870eSAlexander V. Chernikov #include <netinet6/in6_fib.h>
510433870eSAlexander V. Chernikov
520433870eSAlexander V. Chernikov #include <net/route.h>
530433870eSAlexander V. Chernikov #include <net/route/nhop.h>
540433870eSAlexander V. Chernikov #include <net/route/route_ctl.h>
550433870eSAlexander V. Chernikov #include <net/route/route_var.h>
560433870eSAlexander V. Chernikov #include <net/route/fib_algo.h>
570433870eSAlexander V. Chernikov
580433870eSAlexander V. Chernikov #define CHUNK_SIZE 10000
590433870eSAlexander V. Chernikov
600433870eSAlexander V. Chernikov VNET_DEFINE_STATIC(struct in_addr *, inet_addr_list);
610433870eSAlexander V. Chernikov #define V_inet_addr_list VNET(inet_addr_list)
620433870eSAlexander V. Chernikov VNET_DEFINE_STATIC(int, inet_list_size);
630433870eSAlexander V. Chernikov #define V_inet_list_size VNET(inet_list_size)
640433870eSAlexander V. Chernikov
650433870eSAlexander V. Chernikov VNET_DEFINE_STATIC(struct in6_addr *, inet6_addr_list);
660433870eSAlexander V. Chernikov #define V_inet6_addr_list VNET(inet6_addr_list)
670433870eSAlexander V. Chernikov VNET_DEFINE_STATIC(int, inet6_list_size);
680433870eSAlexander V. Chernikov #define V_inet6_list_size VNET(inet6_list_size)
690433870eSAlexander V. Chernikov
700433870eSAlexander V. Chernikov SYSCTL_DECL(_net_route);
710433870eSAlexander V. Chernikov SYSCTL_NODE(_net_route, OID_AUTO, test, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
720433870eSAlexander V. Chernikov "Route algorithm lookups");
730433870eSAlexander V. Chernikov
740433870eSAlexander V. Chernikov static int
add_addr(int family,char * addr_str)750433870eSAlexander V. Chernikov add_addr(int family, char *addr_str)
760433870eSAlexander V. Chernikov {
770433870eSAlexander V. Chernikov
780433870eSAlexander V. Chernikov if (family == AF_INET) {
790433870eSAlexander V. Chernikov struct in_addr *paddr_old = V_inet_addr_list;
800433870eSAlexander V. Chernikov int size_old = V_inet_list_size;
810433870eSAlexander V. Chernikov struct in_addr addr;
820433870eSAlexander V. Chernikov
830433870eSAlexander V. Chernikov if (inet_pton(AF_INET, addr_str, &addr) != 1)
840433870eSAlexander V. Chernikov return (EINVAL);
850433870eSAlexander V. Chernikov
860433870eSAlexander V. Chernikov struct in_addr *paddr = mallocarray(size_old + 1,
870433870eSAlexander V. Chernikov sizeof(struct in_addr), M_TEMP, M_ZERO | M_WAITOK);
880433870eSAlexander V. Chernikov
890433870eSAlexander V. Chernikov if (paddr_old != NULL) {
900433870eSAlexander V. Chernikov memcpy(paddr, paddr_old, size_old * sizeof(struct in_addr));
910433870eSAlexander V. Chernikov free(paddr_old, M_TEMP);
920433870eSAlexander V. Chernikov }
930433870eSAlexander V. Chernikov paddr[size_old] = addr;
940433870eSAlexander V. Chernikov
950433870eSAlexander V. Chernikov V_inet_addr_list = paddr;
960433870eSAlexander V. Chernikov V_inet_list_size = size_old + 1;
970433870eSAlexander V. Chernikov inet_ntop(AF_INET, &addr, addr_str, sizeof(addr_str));
980433870eSAlexander V. Chernikov } else if (family == AF_INET6) {
990433870eSAlexander V. Chernikov struct in6_addr *paddr_old = V_inet6_addr_list;
1000433870eSAlexander V. Chernikov int size_old = V_inet6_list_size;
1010433870eSAlexander V. Chernikov struct in6_addr addr6;
1020433870eSAlexander V. Chernikov
1030433870eSAlexander V. Chernikov if (inet_pton(AF_INET6, addr_str, &addr6) != 1)
1040433870eSAlexander V. Chernikov return (EINVAL);
1050433870eSAlexander V. Chernikov
1060433870eSAlexander V. Chernikov struct in6_addr *paddr = mallocarray(size_old + 1,
1070433870eSAlexander V. Chernikov sizeof(struct in6_addr), M_TEMP, M_ZERO | M_WAITOK);
1080433870eSAlexander V. Chernikov
1090433870eSAlexander V. Chernikov if (paddr_old != NULL) {
1100433870eSAlexander V. Chernikov memcpy(paddr, paddr_old, size_old * sizeof(struct in6_addr));
1110433870eSAlexander V. Chernikov free(paddr_old, M_TEMP);
1120433870eSAlexander V. Chernikov }
1130433870eSAlexander V. Chernikov paddr[size_old] = addr6;
1140433870eSAlexander V. Chernikov
1150433870eSAlexander V. Chernikov V_inet6_addr_list = paddr;
1160433870eSAlexander V. Chernikov V_inet6_list_size = size_old + 1;
1170433870eSAlexander V. Chernikov inet_ntop(AF_INET6, &addr6, addr_str, sizeof(addr_str));
1180433870eSAlexander V. Chernikov }
1190433870eSAlexander V. Chernikov
1200433870eSAlexander V. Chernikov return (0);
1210433870eSAlexander V. Chernikov }
1220433870eSAlexander V. Chernikov
1230433870eSAlexander V. Chernikov static int
add_addr_sysctl_handler(struct sysctl_oid * oidp,struct sysctl_req * req,int family)1240433870eSAlexander V. Chernikov add_addr_sysctl_handler(struct sysctl_oid *oidp, struct sysctl_req *req, int family)
1250433870eSAlexander V. Chernikov {
1260433870eSAlexander V. Chernikov char addr_str[INET6_ADDRSTRLEN];
1270433870eSAlexander V. Chernikov int error;
1280433870eSAlexander V. Chernikov
1290433870eSAlexander V. Chernikov bzero(addr_str, sizeof(addr_str));
1300433870eSAlexander V. Chernikov
1310433870eSAlexander V. Chernikov error = sysctl_handle_string(oidp, addr_str, sizeof(addr_str), req);
1320433870eSAlexander V. Chernikov if (error != 0 || req->newptr == NULL)
1330433870eSAlexander V. Chernikov return (error);
1340433870eSAlexander V. Chernikov
1350433870eSAlexander V. Chernikov error = add_addr(family, addr_str);
1360433870eSAlexander V. Chernikov
1370433870eSAlexander V. Chernikov return (0);
1380433870eSAlexander V. Chernikov }
1390433870eSAlexander V. Chernikov
1400433870eSAlexander V. Chernikov static int
add_inet_addr_sysctl_handler(SYSCTL_HANDLER_ARGS)1410433870eSAlexander V. Chernikov add_inet_addr_sysctl_handler(SYSCTL_HANDLER_ARGS)
1420433870eSAlexander V. Chernikov {
1430433870eSAlexander V. Chernikov
1440433870eSAlexander V. Chernikov return (add_addr_sysctl_handler(oidp, req, AF_INET));
1450433870eSAlexander V. Chernikov }
1460433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, add_inet_addr,
1470433870eSAlexander V. Chernikov CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
1480433870eSAlexander V. Chernikov add_inet_addr_sysctl_handler, "A", "Set");
1490433870eSAlexander V. Chernikov
1500433870eSAlexander V. Chernikov static int
add_inet6_addr_sysctl_handler(SYSCTL_HANDLER_ARGS)1510433870eSAlexander V. Chernikov add_inet6_addr_sysctl_handler(SYSCTL_HANDLER_ARGS)
1520433870eSAlexander V. Chernikov {
1530433870eSAlexander V. Chernikov
1540433870eSAlexander V. Chernikov return (add_addr_sysctl_handler(oidp, req, AF_INET6));
1550433870eSAlexander V. Chernikov }
1560433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, add_inet6_addr,
1570433870eSAlexander V. Chernikov CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
1580433870eSAlexander V. Chernikov add_inet6_addr_sysctl_handler, "A", "Set");
1590433870eSAlexander V. Chernikov
1600433870eSAlexander V. Chernikov static uint64_t
run_test_inet_one_pass(uint32_t fibnum)1614a77a9b6SAlexander V. Chernikov run_test_inet_one_pass(uint32_t fibnum)
1620433870eSAlexander V. Chernikov {
1630433870eSAlexander V. Chernikov /* Assume epoch */
1640433870eSAlexander V. Chernikov int sz = V_inet_list_size;
1650433870eSAlexander V. Chernikov int tries = CHUNK_SIZE / sz;
1660433870eSAlexander V. Chernikov const struct in_addr *a = V_inet_addr_list;
1670433870eSAlexander V. Chernikov uint64_t count = 0;
1680433870eSAlexander V. Chernikov
1690433870eSAlexander V. Chernikov for (int pass = 0; pass < tries; pass++) {
1700433870eSAlexander V. Chernikov for (int i = 0; i < sz; i++) {
1714a77a9b6SAlexander V. Chernikov fib4_lookup(fibnum, a[i], 0, NHR_NONE, 0);
1720433870eSAlexander V. Chernikov count++;
1730433870eSAlexander V. Chernikov }
1740433870eSAlexander V. Chernikov }
1750433870eSAlexander V. Chernikov return (count);
1760433870eSAlexander V. Chernikov }
1770433870eSAlexander V. Chernikov
1780433870eSAlexander V. Chernikov static int
run_test_inet(SYSCTL_HANDLER_ARGS)1790433870eSAlexander V. Chernikov run_test_inet(SYSCTL_HANDLER_ARGS)
1800433870eSAlexander V. Chernikov {
1810433870eSAlexander V. Chernikov struct epoch_tracker et;
1820433870eSAlexander V. Chernikov
1830433870eSAlexander V. Chernikov int count = 0;
1840433870eSAlexander V. Chernikov int error = sysctl_handle_int(oidp, &count, 0, req);
1850433870eSAlexander V. Chernikov if (error != 0)
1860433870eSAlexander V. Chernikov return (error);
1870433870eSAlexander V. Chernikov
1880433870eSAlexander V. Chernikov if (count == 0)
1890433870eSAlexander V. Chernikov return (0);
1900433870eSAlexander V. Chernikov
1910433870eSAlexander V. Chernikov if (V_inet_list_size <= 0)
1920433870eSAlexander V. Chernikov return (ENOENT);
1930433870eSAlexander V. Chernikov
1940433870eSAlexander V. Chernikov printf("run: %d packets vnet %p\n", count, curvnet);
1950433870eSAlexander V. Chernikov if (count < CHUNK_SIZE)
1960433870eSAlexander V. Chernikov count = CHUNK_SIZE;
1970433870eSAlexander V. Chernikov
1980433870eSAlexander V. Chernikov struct timespec ts_pre, ts_post;
1990433870eSAlexander V. Chernikov int64_t pass_diff, total_diff = 0;
2000433870eSAlexander V. Chernikov uint64_t pass_packets, total_packets = 0;
2014a77a9b6SAlexander V. Chernikov uint32_t fibnum = curthread->td_proc->p_fibnum;
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);
2064a77a9b6SAlexander V. Chernikov pass_packets = run_test_inet_one_pass(fibnum);
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
run_test_inet6_one_pass(uint32_t fibnum)2264a77a9b6SAlexander V. Chernikov run_test_inet6_one_pass(uint32_t fibnum)
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++) {
2364a77a9b6SAlexander V. Chernikov fib6_lookup(fibnum, &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
run_test_inet6(SYSCTL_HANDLER_ARGS)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;
2664a77a9b6SAlexander V. Chernikov uint32_t fibnum = curthread->td_proc->p_fibnum;
2670433870eSAlexander V. Chernikov
2680433870eSAlexander V. Chernikov for (int pass = 0; pass < count / CHUNK_SIZE; pass++) {
2690433870eSAlexander V. Chernikov NET_EPOCH_ENTER(et);
2700433870eSAlexander V. Chernikov nanouptime(&ts_pre);
2714a77a9b6SAlexander V. Chernikov pass_packets = run_test_inet6_one_pass(fibnum);
2720433870eSAlexander V. Chernikov nanouptime(&ts_post);
2730433870eSAlexander V. Chernikov NET_EPOCH_EXIT(et);
2740433870eSAlexander V. Chernikov
2750433870eSAlexander V. Chernikov pass_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
2760433870eSAlexander V. Chernikov (ts_post.tv_nsec - ts_pre.tv_nsec);
2770433870eSAlexander V. Chernikov total_diff += pass_diff;
2780433870eSAlexander V. Chernikov total_packets += pass_packets;
2790433870eSAlexander V. Chernikov }
2800433870eSAlexander V. Chernikov
2810433870eSAlexander V. Chernikov printf("%zu packets in %zu nanoseconds, %zu pps\n",
2820433870eSAlexander V. Chernikov total_packets, total_diff, total_packets * 1000000000 / total_diff);
2830433870eSAlexander V. Chernikov
2840433870eSAlexander V. Chernikov return (0);
2850433870eSAlexander V. Chernikov }
2860433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet6,
28774935ce8SAlexander V. Chernikov CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
2880433870eSAlexander V. Chernikov 0, 0, run_test_inet6, "I", "Execute fib6_lookup test");
2890433870eSAlexander V. Chernikov
2900433870eSAlexander V. Chernikov static bool
cmp_dst(uint32_t fibnum,struct in_addr a)2910433870eSAlexander V. Chernikov cmp_dst(uint32_t fibnum, struct in_addr a)
2920433870eSAlexander V. Chernikov {
2930433870eSAlexander V. Chernikov struct nhop_object *nh_fib;
2940433870eSAlexander V. Chernikov struct rtentry *rt;
2950433870eSAlexander V. Chernikov struct route_nhop_data rnd = {};
2960433870eSAlexander V. Chernikov
2970433870eSAlexander V. Chernikov nh_fib = fib4_lookup(fibnum, a, 0, NHR_NONE, 0);
2980433870eSAlexander V. Chernikov rt = fib4_lookup_rt(fibnum, a, 0, NHR_NONE, &rnd);
2990433870eSAlexander V. Chernikov
3000433870eSAlexander V. Chernikov if (nh_fib == NULL && rt == NULL) {
3010433870eSAlexander V. Chernikov return (true);
3020433870eSAlexander V. Chernikov } else if (nh_fib == nhop_select(rnd.rnd_nhop, 0)) {
3030433870eSAlexander V. Chernikov return (true);
3040433870eSAlexander V. Chernikov }
3050433870eSAlexander V. Chernikov
3060433870eSAlexander V. Chernikov struct in_addr dst;
3070433870eSAlexander V. Chernikov int plen;
3080433870eSAlexander V. Chernikov uint32_t scopeid;
3090433870eSAlexander V. Chernikov char key_str[INET_ADDRSTRLEN], dst_str[INET_ADDRSTRLEN];
3100433870eSAlexander V. Chernikov
3110433870eSAlexander V. Chernikov inet_ntop(AF_INET, &a, key_str, sizeof(key_str));
3120433870eSAlexander V. Chernikov if (rnd.rnd_nhop == NULL) {
3130433870eSAlexander V. Chernikov printf("[RT BUG] lookup for %s: RIB: ENOENT FIB: nh=%u\n",
3140433870eSAlexander V. Chernikov key_str, nhop_get_idx(nh_fib));
3150433870eSAlexander V. Chernikov } else {
3160433870eSAlexander V. Chernikov rt_get_inet_prefix_plen(rt, &dst, &plen, &scopeid);
3170433870eSAlexander V. Chernikov inet_ntop(AF_INET, &dst, dst_str, sizeof(dst_str));
3180433870eSAlexander V. Chernikov printf("[RT BUG] lookup for %s: RIB: %s/%d,nh=%u FIB: nh=%u\n",
3190433870eSAlexander V. Chernikov key_str, dst_str, plen,
3200433870eSAlexander V. Chernikov nhop_get_idx(nhop_select(rnd.rnd_nhop, 0)),
321cbfba56cSAlexander V. Chernikov nh_fib ? nhop_get_idx(nh_fib) : 0);
322cbfba56cSAlexander V. Chernikov }
323cbfba56cSAlexander V. Chernikov
324cbfba56cSAlexander V. Chernikov return (false);
325cbfba56cSAlexander V. Chernikov }
326cbfba56cSAlexander V. Chernikov
327cbfba56cSAlexander V. Chernikov static bool
cmp_dst6(uint32_t fibnum,const struct in6_addr * a)328cbfba56cSAlexander V. Chernikov cmp_dst6(uint32_t fibnum, const struct in6_addr *a)
329cbfba56cSAlexander V. Chernikov {
330cbfba56cSAlexander V. Chernikov struct nhop_object *nh_fib;
331cbfba56cSAlexander V. Chernikov struct rtentry *rt;
332cbfba56cSAlexander V. Chernikov struct route_nhop_data rnd = {};
333cbfba56cSAlexander V. Chernikov
334cbfba56cSAlexander V. Chernikov nh_fib = fib6_lookup(fibnum, a, 0, NHR_NONE, 0);
335cbfba56cSAlexander V. Chernikov rt = fib6_lookup_rt(fibnum, a, 0, NHR_NONE, &rnd);
336cbfba56cSAlexander V. Chernikov
337cbfba56cSAlexander V. Chernikov if (nh_fib == NULL && rt == NULL) {
338cbfba56cSAlexander V. Chernikov return (true);
339cbfba56cSAlexander V. Chernikov } else if (nh_fib == nhop_select(rnd.rnd_nhop, 0)) {
340cbfba56cSAlexander V. Chernikov return (true);
341cbfba56cSAlexander V. Chernikov }
342cbfba56cSAlexander V. Chernikov
343cbfba56cSAlexander V. Chernikov struct in6_addr dst;
344cbfba56cSAlexander V. Chernikov int plen;
345cbfba56cSAlexander V. Chernikov uint32_t scopeid;
346cbfba56cSAlexander V. Chernikov char key_str[INET6_ADDRSTRLEN], dst_str[INET6_ADDRSTRLEN];
347cbfba56cSAlexander V. Chernikov
348cbfba56cSAlexander V. Chernikov inet_ntop(AF_INET6, a, key_str, sizeof(key_str));
349cbfba56cSAlexander V. Chernikov if (rnd.rnd_nhop == NULL) {
350cbfba56cSAlexander V. Chernikov printf("[RT BUG] lookup for %s: RIB: ENOENT FIB: nh=%u\n",
351cbfba56cSAlexander V. Chernikov key_str, nhop_get_idx(nh_fib));
352cbfba56cSAlexander V. Chernikov } else {
353cbfba56cSAlexander V. Chernikov rt_get_inet6_prefix_plen(rt, &dst, &plen, &scopeid);
354cbfba56cSAlexander V. Chernikov inet_ntop(AF_INET6, &dst, dst_str, sizeof(dst_str));
355cbfba56cSAlexander V. Chernikov printf("[RT BUG] lookup for %s: RIB: %s/%d,nh=%u FIB: nh=%u\n",
356cbfba56cSAlexander V. Chernikov key_str, dst_str, plen,
357cbfba56cSAlexander V. Chernikov nhop_get_idx(nhop_select(rnd.rnd_nhop, 0)),
358cbfba56cSAlexander V. Chernikov nh_fib ? nhop_get_idx(nh_fib) : 0);
3590433870eSAlexander V. Chernikov }
3600433870eSAlexander V. Chernikov
3610433870eSAlexander V. Chernikov return (false);
3620433870eSAlexander V. Chernikov }
3630433870eSAlexander V. Chernikov
3640433870eSAlexander V. Chernikov /* Random lookups: correctness verification */
3650433870eSAlexander V. Chernikov static uint64_t
run_test_inet_one_pass_random(uint32_t fibnum)3664a77a9b6SAlexander V. Chernikov run_test_inet_one_pass_random(uint32_t fibnum)
3670433870eSAlexander V. Chernikov {
3680433870eSAlexander V. Chernikov /* Assume epoch */
3690433870eSAlexander V. Chernikov struct in_addr a[64];
3700433870eSAlexander V. Chernikov int sz = 64;
3710433870eSAlexander V. Chernikov uint64_t count = 0;
3720433870eSAlexander V. Chernikov
3730433870eSAlexander V. Chernikov for (int pass = 0; pass < CHUNK_SIZE / sz; pass++) {
3740433870eSAlexander V. Chernikov arc4random_buf(a, sizeof(a));
3750433870eSAlexander V. Chernikov for (int i = 0; i < sz; i++) {
3764a77a9b6SAlexander V. Chernikov if (!cmp_dst(fibnum, a[i]))
3770433870eSAlexander V. Chernikov return (0);
3780433870eSAlexander V. Chernikov count++;
3790433870eSAlexander V. Chernikov }
3800433870eSAlexander V. Chernikov }
3810433870eSAlexander V. Chernikov return (count);
3820433870eSAlexander V. Chernikov }
3830433870eSAlexander V. Chernikov
3840433870eSAlexander V. Chernikov static int
run_test_inet_random(SYSCTL_HANDLER_ARGS)3850433870eSAlexander V. Chernikov run_test_inet_random(SYSCTL_HANDLER_ARGS)
3860433870eSAlexander V. Chernikov {
3870433870eSAlexander V. Chernikov struct epoch_tracker et;
3880433870eSAlexander V. Chernikov
3890433870eSAlexander V. Chernikov int count = 0;
3900433870eSAlexander V. Chernikov int error = sysctl_handle_int(oidp, &count, 0, req);
3910433870eSAlexander V. Chernikov if (error != 0)
3920433870eSAlexander V. Chernikov return (error);
3930433870eSAlexander V. Chernikov
3940433870eSAlexander V. Chernikov if (count == 0)
3950433870eSAlexander V. Chernikov return (0);
3960433870eSAlexander V. Chernikov
3970433870eSAlexander V. Chernikov if (count < CHUNK_SIZE)
3980433870eSAlexander V. Chernikov count = CHUNK_SIZE;
3990433870eSAlexander V. Chernikov
4000433870eSAlexander V. Chernikov struct timespec ts_pre, ts_post;
4010433870eSAlexander V. Chernikov int64_t pass_diff, total_diff = 1;
4020433870eSAlexander V. Chernikov uint64_t pass_packets, total_packets = 0;
4034a77a9b6SAlexander V. Chernikov uint32_t fibnum = curthread->td_proc->p_fibnum;
4040433870eSAlexander V. Chernikov
4050433870eSAlexander V. Chernikov for (int pass = 0; pass < count / CHUNK_SIZE; pass++) {
4060433870eSAlexander V. Chernikov NET_EPOCH_ENTER(et);
4070433870eSAlexander V. Chernikov nanouptime(&ts_pre);
4084a77a9b6SAlexander V. Chernikov pass_packets = run_test_inet_one_pass_random(fibnum);
4090433870eSAlexander V. Chernikov nanouptime(&ts_post);
4100433870eSAlexander V. Chernikov NET_EPOCH_EXIT(et);
4110433870eSAlexander V. Chernikov
4120433870eSAlexander V. Chernikov pass_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
4130433870eSAlexander V. Chernikov (ts_post.tv_nsec - ts_pre.tv_nsec);
4140433870eSAlexander V. Chernikov total_diff += pass_diff;
4150433870eSAlexander V. Chernikov total_packets += pass_packets;
4160433870eSAlexander V. Chernikov
4170433870eSAlexander V. Chernikov if (pass_packets == 0)
4180433870eSAlexander V. Chernikov break;
4190433870eSAlexander V. Chernikov }
4200433870eSAlexander V. Chernikov
4210433870eSAlexander V. Chernikov /* Signal error to userland */
4220433870eSAlexander V. Chernikov if (pass_packets == 0)
4230433870eSAlexander V. Chernikov return (EINVAL);
4240433870eSAlexander V. Chernikov
4250433870eSAlexander V. Chernikov printf("%zu packets in %zu nanoseconds, %zu pps\n",
4260433870eSAlexander V. Chernikov total_packets, total_diff, total_packets * 1000000000 / total_diff);
4270433870eSAlexander V. Chernikov
4280433870eSAlexander V. Chernikov return (0);
4290433870eSAlexander V. Chernikov }
4300433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet_random,
43174935ce8SAlexander V. Chernikov CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
4320433870eSAlexander V. Chernikov 0, 0, run_test_inet_random, "I", "Execute fib4_lookup random check tests");
4330433870eSAlexander V. Chernikov
4340433870eSAlexander V. Chernikov
4350433870eSAlexander V. Chernikov struct inet_array {
4360433870eSAlexander V. Chernikov uint32_t alloc_items;
4370433870eSAlexander V. Chernikov uint32_t num_items;
4384a77a9b6SAlexander V. Chernikov uint32_t rnh_prefixes;
4390433870eSAlexander V. Chernikov int error;
4404a77a9b6SAlexander V. Chernikov struct in_addr *arr;
4410433870eSAlexander V. Chernikov };
4420433870eSAlexander V. Chernikov
4430433870eSAlexander V. Chernikov /*
4440433870eSAlexander V. Chernikov * For each prefix, add the following records to the lookup array:
4450433870eSAlexander V. Chernikov * * prefix-1, prefix, prefix + 1, prefix_end, prefix_end + 1
4460433870eSAlexander V. Chernikov */
4470433870eSAlexander V. Chernikov static int
add_prefix(struct rtentry * rt,void * _data)4480433870eSAlexander V. Chernikov add_prefix(struct rtentry *rt, void *_data)
4490433870eSAlexander V. Chernikov {
4500433870eSAlexander V. Chernikov struct inet_array *pa = (struct inet_array *)_data;
4510433870eSAlexander V. Chernikov struct in_addr addr;
4520433870eSAlexander V. Chernikov int plen;
4530433870eSAlexander V. Chernikov uint32_t scopeid, haddr;
4540433870eSAlexander V. Chernikov
4554a77a9b6SAlexander V. Chernikov pa->rnh_prefixes++;
4564a77a9b6SAlexander V. Chernikov
4570433870eSAlexander V. Chernikov if (pa->num_items + 5 >= pa->alloc_items) {
4580433870eSAlexander V. Chernikov if (pa->error == 0)
4594a77a9b6SAlexander V. Chernikov pa->error = ENOSPC;
4600433870eSAlexander V. Chernikov return (0);
4610433870eSAlexander V. Chernikov }
4620433870eSAlexander V. Chernikov
4630433870eSAlexander V. Chernikov rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid);
4640433870eSAlexander V. Chernikov
4650433870eSAlexander V. Chernikov pa->arr[pa->num_items++] = addr;
4660433870eSAlexander V. Chernikov haddr = ntohl(addr.s_addr);
4670433870eSAlexander V. Chernikov if (haddr > 0) {
4680433870eSAlexander V. Chernikov pa->arr[pa->num_items++].s_addr = htonl(haddr - 1);
4690433870eSAlexander V. Chernikov pa->arr[pa->num_items++].s_addr = htonl(haddr + 1);
4700433870eSAlexander V. Chernikov /* assume mask != 0 */
4710433870eSAlexander V. Chernikov uint32_t mlen = (1 << (32 - plen)) - 1;
4720433870eSAlexander V. Chernikov pa->arr[pa->num_items++].s_addr = htonl(haddr + mlen);
4730433870eSAlexander V. Chernikov /* can overflow, but who cares */
4740433870eSAlexander V. Chernikov pa->arr[pa->num_items++].s_addr = htonl(haddr + mlen + 1);
4750433870eSAlexander V. Chernikov }
4760433870eSAlexander V. Chernikov
4770433870eSAlexander V. Chernikov return (0);
4780433870eSAlexander V. Chernikov }
4790433870eSAlexander V. Chernikov
4800433870eSAlexander V. Chernikov static bool
prepare_list(uint32_t fibnum,struct inet_array * pa)4810433870eSAlexander V. Chernikov prepare_list(uint32_t fibnum, struct inet_array *pa)
4820433870eSAlexander V. Chernikov {
4830433870eSAlexander V. Chernikov struct rib_head *rh;
4840433870eSAlexander V. Chernikov
4850433870eSAlexander V. Chernikov rh = rt_tables_get_rnh(fibnum, AF_INET);
4860433870eSAlexander V. Chernikov
4874a77a9b6SAlexander V. Chernikov uint32_t num_prefixes = rh->rnh_prefixes;
4880433870eSAlexander V. Chernikov bzero(pa, sizeof(struct inet_array));
4894a77a9b6SAlexander V. Chernikov pa->alloc_items = (num_prefixes + 10) * 5;
4904a77a9b6SAlexander V. Chernikov pa->arr = mallocarray(pa->alloc_items, sizeof(struct in_addr),
4910433870eSAlexander V. Chernikov M_TEMP, M_ZERO | M_WAITOK);
4920433870eSAlexander V. Chernikov
4934a77a9b6SAlexander V. Chernikov rib_walk(fibnum, AF_INET, false, add_prefix, pa);
4944a77a9b6SAlexander V. Chernikov
4954a77a9b6SAlexander V. Chernikov if (pa->error != 0) {
4964a77a9b6SAlexander V. Chernikov printf("prefixes: old: %u, current: %u, walked: %u, allocated: %u\n",
4974a77a9b6SAlexander V. Chernikov num_prefixes, rh->rnh_prefixes, pa->rnh_prefixes, pa->alloc_items);
4984a77a9b6SAlexander V. Chernikov }
4990433870eSAlexander V. Chernikov
5000433870eSAlexander V. Chernikov return (pa->error == 0);
5010433870eSAlexander V. Chernikov }
5020433870eSAlexander V. Chernikov
5030433870eSAlexander V. Chernikov static int
run_test_inet_scan(SYSCTL_HANDLER_ARGS)5040433870eSAlexander V. Chernikov run_test_inet_scan(SYSCTL_HANDLER_ARGS)
5050433870eSAlexander V. Chernikov {
5060433870eSAlexander V. Chernikov struct epoch_tracker et;
5070433870eSAlexander V. Chernikov
5080433870eSAlexander V. Chernikov int count = 0;
5090433870eSAlexander V. Chernikov int error = sysctl_handle_int(oidp, &count, 0, req);
5100433870eSAlexander V. Chernikov if (error != 0)
5110433870eSAlexander V. Chernikov return (error);
5120433870eSAlexander V. Chernikov
5130433870eSAlexander V. Chernikov if (count == 0)
5140433870eSAlexander V. Chernikov return (0);
5150433870eSAlexander V. Chernikov
5160433870eSAlexander V. Chernikov struct inet_array pa = {};
5174a77a9b6SAlexander V. Chernikov uint32_t fibnum = curthread->td_proc->p_fibnum;
5180433870eSAlexander V. Chernikov
5194a77a9b6SAlexander V. Chernikov if (!prepare_list(fibnum, &pa))
5200433870eSAlexander V. Chernikov return (pa.error);
5210433870eSAlexander V. Chernikov
5220433870eSAlexander V. Chernikov struct timespec ts_pre, ts_post;
5230433870eSAlexander V. Chernikov int64_t total_diff = 1;
5240433870eSAlexander V. Chernikov uint64_t total_packets = 0;
5254a77a9b6SAlexander V. Chernikov int failure_count = 0;
5260433870eSAlexander V. Chernikov
5270433870eSAlexander V. Chernikov NET_EPOCH_ENTER(et);
5280433870eSAlexander V. Chernikov nanouptime(&ts_pre);
5290433870eSAlexander V. Chernikov for (int i = 0; i < pa.num_items; i++) {
5304a77a9b6SAlexander V. Chernikov if (!cmp_dst(fibnum, pa.arr[i])) {
5314a77a9b6SAlexander V. Chernikov failure_count++;
5320433870eSAlexander V. Chernikov }
5330433870eSAlexander V. Chernikov total_packets++;
5340433870eSAlexander V. Chernikov }
5350433870eSAlexander V. Chernikov nanouptime(&ts_post);
5360433870eSAlexander V. Chernikov NET_EPOCH_EXIT(et);
5370433870eSAlexander V. Chernikov
5380433870eSAlexander V. Chernikov if (pa.arr != NULL)
5390433870eSAlexander V. Chernikov free(pa.arr, M_TEMP);
5400433870eSAlexander V. Chernikov
5410433870eSAlexander V. Chernikov /* Signal error to userland */
5424a77a9b6SAlexander V. Chernikov if (failure_count > 0) {
5434a77a9b6SAlexander V. Chernikov printf("[RT ERROR] total failures: %d\n", failure_count);
5444a77a9b6SAlexander V. Chernikov return (EINVAL);
5454a77a9b6SAlexander V. Chernikov }
5460433870eSAlexander V. Chernikov
5470433870eSAlexander V. Chernikov total_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
5480433870eSAlexander V. Chernikov (ts_post.tv_nsec - ts_pre.tv_nsec);
5490433870eSAlexander V. Chernikov printf("%zu packets in %zu nanoseconds, %zu pps\n",
5500433870eSAlexander V. Chernikov total_packets, total_diff, total_packets * 1000000000 / total_diff);
5510433870eSAlexander V. Chernikov
5520433870eSAlexander V. Chernikov return (0);
5530433870eSAlexander V. Chernikov }
5540433870eSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet_scan,
55574935ce8SAlexander V. Chernikov CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
5560433870eSAlexander V. Chernikov 0, 0, run_test_inet_scan, "I", "Execute fib4_lookup scan tests");
5570433870eSAlexander V. Chernikov
558cbfba56cSAlexander V. Chernikov struct inet6_array {
559cbfba56cSAlexander V. Chernikov uint32_t alloc_items;
560cbfba56cSAlexander V. Chernikov uint32_t num_items;
561cbfba56cSAlexander V. Chernikov uint32_t rnh_prefixes;
562cbfba56cSAlexander V. Chernikov int error;
563cbfba56cSAlexander V. Chernikov struct in6_addr *arr;
564cbfba56cSAlexander V. Chernikov };
565cbfba56cSAlexander V. Chernikov
566cbfba56cSAlexander V. Chernikov static bool
safe_add(uint32_t * v,uint32_t inc)567cbfba56cSAlexander V. Chernikov safe_add(uint32_t *v, uint32_t inc)
568cbfba56cSAlexander V. Chernikov {
569cbfba56cSAlexander V. Chernikov if (*v < (UINT32_MAX - inc)) {
570cbfba56cSAlexander V. Chernikov *v += inc;
571cbfba56cSAlexander V. Chernikov return (true);
572cbfba56cSAlexander V. Chernikov } else {
573cbfba56cSAlexander V. Chernikov *v -= (UINT32_MAX - inc + 1);
574cbfba56cSAlexander V. Chernikov return (false);
575cbfba56cSAlexander V. Chernikov }
576cbfba56cSAlexander V. Chernikov }
577cbfba56cSAlexander V. Chernikov
578cbfba56cSAlexander V. Chernikov static bool
safe_dec(uint32_t * v,uint32_t inc)579cbfba56cSAlexander V. Chernikov safe_dec(uint32_t *v, uint32_t inc)
580cbfba56cSAlexander V. Chernikov {
581cbfba56cSAlexander V. Chernikov if (*v >= inc) {
582cbfba56cSAlexander V. Chernikov *v -= inc;
583cbfba56cSAlexander V. Chernikov return (true);
584cbfba56cSAlexander V. Chernikov } else {
585cbfba56cSAlexander V. Chernikov *v += (UINT32_MAX - inc + 1);
586cbfba56cSAlexander V. Chernikov return (false);
587cbfba56cSAlexander V. Chernikov }
588cbfba56cSAlexander V. Chernikov }
589cbfba56cSAlexander V. Chernikov
590cbfba56cSAlexander V. Chernikov static void
inc_prefix6(struct in6_addr * addr,int inc)591cbfba56cSAlexander V. Chernikov inc_prefix6(struct in6_addr *addr, int inc)
592cbfba56cSAlexander V. Chernikov {
593cbfba56cSAlexander V. Chernikov for (int i = 0; i < 4; i++) {
594cbfba56cSAlexander V. Chernikov uint32_t v = ntohl(addr->s6_addr32[3 - i]);
595cbfba56cSAlexander V. Chernikov bool ret = safe_add(&v, inc);
596cbfba56cSAlexander V. Chernikov addr->s6_addr32[3 - i] = htonl(v);
597cbfba56cSAlexander V. Chernikov if (ret)
598cbfba56cSAlexander V. Chernikov return;
599cbfba56cSAlexander V. Chernikov inc = 1;
600cbfba56cSAlexander V. Chernikov }
601cbfba56cSAlexander V. Chernikov }
602cbfba56cSAlexander V. Chernikov
603cbfba56cSAlexander V. Chernikov static void
dec_prefix6(struct in6_addr * addr,int dec)604cbfba56cSAlexander V. Chernikov dec_prefix6(struct in6_addr *addr, int dec)
605cbfba56cSAlexander V. Chernikov {
606cbfba56cSAlexander V. Chernikov for (int i = 0; i < 4; i++) {
607cbfba56cSAlexander V. Chernikov uint32_t v = ntohl(addr->s6_addr32[3 - i]);
608cbfba56cSAlexander V. Chernikov bool ret = safe_dec(&v, dec);
609cbfba56cSAlexander V. Chernikov addr->s6_addr32[3 - i] = htonl(v);
610cbfba56cSAlexander V. Chernikov if (ret)
611cbfba56cSAlexander V. Chernikov return;
612cbfba56cSAlexander V. Chernikov dec = 1;
613cbfba56cSAlexander V. Chernikov }
614cbfba56cSAlexander V. Chernikov }
615cbfba56cSAlexander V. Chernikov
616cbfba56cSAlexander V. Chernikov static void
ipv6_writemask(struct in6_addr * addr6,uint8_t mask)617cbfba56cSAlexander V. Chernikov ipv6_writemask(struct in6_addr *addr6, uint8_t mask)
618cbfba56cSAlexander V. Chernikov {
619cbfba56cSAlexander V. Chernikov uint32_t *cp;
620cbfba56cSAlexander V. Chernikov
621cbfba56cSAlexander V. Chernikov for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
622cbfba56cSAlexander V. Chernikov *cp++ = 0xFFFFFFFF;
623cbfba56cSAlexander V. Chernikov if (mask > 0)
624cbfba56cSAlexander V. Chernikov *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
625cbfba56cSAlexander V. Chernikov }
626cbfba56cSAlexander V. Chernikov
627cbfba56cSAlexander V. Chernikov /*
628cbfba56cSAlexander V. Chernikov * For each prefix, add the following records to the lookup array:
629cbfba56cSAlexander V. Chernikov * * prefix-1, prefix, prefix + 1, prefix_end, prefix_end + 1
630cbfba56cSAlexander V. Chernikov */
631cbfba56cSAlexander V. Chernikov static int
add_prefix6(struct rtentry * rt,void * _data)632cbfba56cSAlexander V. Chernikov add_prefix6(struct rtentry *rt, void *_data)
633cbfba56cSAlexander V. Chernikov {
634cbfba56cSAlexander V. Chernikov struct inet6_array *pa = (struct inet6_array *)_data;
635cbfba56cSAlexander V. Chernikov struct in6_addr addr, naddr;
636cbfba56cSAlexander V. Chernikov int plen;
637cbfba56cSAlexander V. Chernikov uint32_t scopeid;
638cbfba56cSAlexander V. Chernikov
639cbfba56cSAlexander V. Chernikov pa->rnh_prefixes++;
640cbfba56cSAlexander V. Chernikov
641cbfba56cSAlexander V. Chernikov if (pa->num_items + 5 >= pa->alloc_items) {
642cbfba56cSAlexander V. Chernikov if (pa->error == 0)
643cbfba56cSAlexander V. Chernikov pa->error = ENOSPC;
644cbfba56cSAlexander V. Chernikov return (0);
645cbfba56cSAlexander V. Chernikov }
646cbfba56cSAlexander V. Chernikov
647cbfba56cSAlexander V. Chernikov rt_get_inet6_prefix_plen(rt, &addr, &plen, &scopeid);
648cbfba56cSAlexander V. Chernikov
649cbfba56cSAlexander V. Chernikov pa->arr[pa->num_items++] = addr;
650cbfba56cSAlexander V. Chernikov if (!IN6_ARE_ADDR_EQUAL(&addr, &in6addr_any)) {
651cbfba56cSAlexander V. Chernikov naddr = addr;
652cbfba56cSAlexander V. Chernikov dec_prefix6(&naddr, 1);
653cbfba56cSAlexander V. Chernikov pa->arr[pa->num_items++] = naddr;
654cbfba56cSAlexander V. Chernikov naddr = addr;
655cbfba56cSAlexander V. Chernikov inc_prefix6(&naddr, 1);
656cbfba56cSAlexander V. Chernikov pa->arr[pa->num_items++] = naddr;
657cbfba56cSAlexander V. Chernikov
658cbfba56cSAlexander V. Chernikov /* assume mask != 0 */
659cbfba56cSAlexander V. Chernikov struct in6_addr mask6;
660cbfba56cSAlexander V. Chernikov ipv6_writemask(&mask6, plen);
661cbfba56cSAlexander V. Chernikov naddr = addr;
662cbfba56cSAlexander V. Chernikov for (int i = 0; i < 3; i++)
663cbfba56cSAlexander V. Chernikov naddr.s6_addr32[i] = htonl(ntohl(naddr.s6_addr32[i]) | ~ntohl(mask6.s6_addr32[i]));
664cbfba56cSAlexander V. Chernikov
665cbfba56cSAlexander V. Chernikov pa->arr[pa->num_items++] = naddr;
666cbfba56cSAlexander V. Chernikov inc_prefix6(&naddr, 1);
667cbfba56cSAlexander V. Chernikov pa->arr[pa->num_items++] = naddr;
668cbfba56cSAlexander V. Chernikov }
669cbfba56cSAlexander V. Chernikov
670cbfba56cSAlexander V. Chernikov return (0);
671cbfba56cSAlexander V. Chernikov }
672cbfba56cSAlexander V. Chernikov
673cbfba56cSAlexander V. Chernikov static bool
prepare_list6(uint32_t fibnum,struct inet6_array * pa)674cbfba56cSAlexander V. Chernikov prepare_list6(uint32_t fibnum, struct inet6_array *pa)
675cbfba56cSAlexander V. Chernikov {
676cbfba56cSAlexander V. Chernikov struct rib_head *rh;
677cbfba56cSAlexander V. Chernikov
678cbfba56cSAlexander V. Chernikov rh = rt_tables_get_rnh(fibnum, AF_INET6);
679cbfba56cSAlexander V. Chernikov
680cbfba56cSAlexander V. Chernikov uint32_t num_prefixes = rh->rnh_prefixes;
681cbfba56cSAlexander V. Chernikov bzero(pa, sizeof(struct inet6_array));
682cbfba56cSAlexander V. Chernikov pa->alloc_items = (num_prefixes + 10) * 5;
683cbfba56cSAlexander V. Chernikov pa->arr = mallocarray(pa->alloc_items, sizeof(struct in6_addr),
684cbfba56cSAlexander V. Chernikov M_TEMP, M_ZERO | M_WAITOK);
685cbfba56cSAlexander V. Chernikov
686cbfba56cSAlexander V. Chernikov rib_walk(fibnum, AF_INET6, false, add_prefix6, pa);
687cbfba56cSAlexander V. Chernikov
688cbfba56cSAlexander V. Chernikov if (pa->error != 0) {
689cbfba56cSAlexander V. Chernikov printf("prefixes: old: %u, current: %u, walked: %u, allocated: %u\n",
690cbfba56cSAlexander V. Chernikov num_prefixes, rh->rnh_prefixes, pa->rnh_prefixes, pa->alloc_items);
691cbfba56cSAlexander V. Chernikov }
692cbfba56cSAlexander V. Chernikov
693cbfba56cSAlexander V. Chernikov return (pa->error == 0);
694cbfba56cSAlexander V. Chernikov }
695cbfba56cSAlexander V. Chernikov
696cbfba56cSAlexander V. Chernikov static int
run_test_inet6_scan(SYSCTL_HANDLER_ARGS)697cbfba56cSAlexander V. Chernikov run_test_inet6_scan(SYSCTL_HANDLER_ARGS)
698cbfba56cSAlexander V. Chernikov {
699cbfba56cSAlexander V. Chernikov struct epoch_tracker et;
700cbfba56cSAlexander V. Chernikov
701cbfba56cSAlexander V. Chernikov int count = 0;
702cbfba56cSAlexander V. Chernikov int error = sysctl_handle_int(oidp, &count, 0, req);
703cbfba56cSAlexander V. Chernikov if (error != 0)
704cbfba56cSAlexander V. Chernikov return (error);
705cbfba56cSAlexander V. Chernikov
706cbfba56cSAlexander V. Chernikov if (count == 0)
707cbfba56cSAlexander V. Chernikov return (0);
708cbfba56cSAlexander V. Chernikov
709cbfba56cSAlexander V. Chernikov struct inet6_array pa = {};
710cbfba56cSAlexander V. Chernikov uint32_t fibnum = curthread->td_proc->p_fibnum;
711cbfba56cSAlexander V. Chernikov
712cbfba56cSAlexander V. Chernikov if (!prepare_list6(fibnum, &pa))
713cbfba56cSAlexander V. Chernikov return (pa.error);
714cbfba56cSAlexander V. Chernikov
715cbfba56cSAlexander V. Chernikov struct timespec ts_pre, ts_post;
716cbfba56cSAlexander V. Chernikov int64_t total_diff = 1;
717cbfba56cSAlexander V. Chernikov uint64_t total_packets = 0;
718cbfba56cSAlexander V. Chernikov int failure_count = 0;
719cbfba56cSAlexander V. Chernikov
720cbfba56cSAlexander V. Chernikov NET_EPOCH_ENTER(et);
721cbfba56cSAlexander V. Chernikov nanouptime(&ts_pre);
722cbfba56cSAlexander V. Chernikov for (int i = 0; i < pa.num_items; i++) {
723cbfba56cSAlexander V. Chernikov if (!cmp_dst6(fibnum, &pa.arr[i])) {
724cbfba56cSAlexander V. Chernikov failure_count++;
725cbfba56cSAlexander V. Chernikov }
726cbfba56cSAlexander V. Chernikov total_packets++;
727cbfba56cSAlexander V. Chernikov }
728cbfba56cSAlexander V. Chernikov nanouptime(&ts_post);
729cbfba56cSAlexander V. Chernikov NET_EPOCH_EXIT(et);
730cbfba56cSAlexander V. Chernikov
731cbfba56cSAlexander V. Chernikov if (pa.arr != NULL)
732cbfba56cSAlexander V. Chernikov free(pa.arr, M_TEMP);
733cbfba56cSAlexander V. Chernikov
734cbfba56cSAlexander V. Chernikov /* Signal error to userland */
735cbfba56cSAlexander V. Chernikov if (failure_count > 0) {
736cbfba56cSAlexander V. Chernikov printf("[RT ERROR] total failures: %d\n", failure_count);
737cbfba56cSAlexander V. Chernikov return (EINVAL);
738cbfba56cSAlexander V. Chernikov }
739cbfba56cSAlexander V. Chernikov
740cbfba56cSAlexander V. Chernikov total_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
741cbfba56cSAlexander V. Chernikov (ts_post.tv_nsec - ts_pre.tv_nsec);
742cbfba56cSAlexander V. Chernikov printf("%zu packets in %zu nanoseconds, %zu pps\n",
743cbfba56cSAlexander V. Chernikov total_packets, total_diff, total_packets * 1000000000 / total_diff);
744cbfba56cSAlexander V. Chernikov
745cbfba56cSAlexander V. Chernikov return (0);
746cbfba56cSAlexander V. Chernikov }
747cbfba56cSAlexander V. Chernikov SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet6_scan,
748cbfba56cSAlexander V. Chernikov CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
749cbfba56cSAlexander V. Chernikov 0, 0, run_test_inet6_scan, "I", "Execute fib6_lookup scan tests");
750cbfba56cSAlexander V. Chernikov
751b6f8436bSMarko Zec #define LPS_SEQ 0x1
752b6f8436bSMarko Zec #define LPS_ANN 0x2
753b6f8436bSMarko Zec #define LPS_REP 0x4
754b6f8436bSMarko Zec
755b6f8436bSMarko Zec struct lps_walk_state {
756b6f8436bSMarko Zec uint32_t *keys;
757b6f8436bSMarko Zec int pos;
758b6f8436bSMarko Zec int lim;
759b6f8436bSMarko Zec };
760b6f8436bSMarko Zec
761b6f8436bSMarko Zec static int
reduce_keys(struct rtentry * rt,void * _data)762b6f8436bSMarko Zec reduce_keys(struct rtentry *rt, void *_data)
763b6f8436bSMarko Zec {
764b6f8436bSMarko Zec struct lps_walk_state *wa = (struct lps_walk_state *) _data;
765b6f8436bSMarko Zec struct in_addr addr;
766b6f8436bSMarko Zec uint32_t scopeid;
767b6f8436bSMarko Zec int plen;
768b6f8436bSMarko Zec
769b6f8436bSMarko Zec rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid);
770b6f8436bSMarko Zec wa->keys[wa->pos] = ntohl(addr.s_addr) |
771b6f8436bSMarko Zec (wa->keys[wa->pos] & ~(0xffffffffU << (32 - plen)));
772b6f8436bSMarko Zec
773b6f8436bSMarko Zec wa->pos++;
774b6f8436bSMarko Zec return (wa->pos == wa->lim);
775b6f8436bSMarko Zec }
776b6f8436bSMarko Zec
777b8598e2fSAlexander V. Chernikov static int
rnd_lps(SYSCTL_HANDLER_ARGS)778b8598e2fSAlexander V. Chernikov rnd_lps(SYSCTL_HANDLER_ARGS)
779b8598e2fSAlexander V. Chernikov {
780b8598e2fSAlexander V. Chernikov struct epoch_tracker et;
781a43104ebSMarko Zec struct in_addr key;
782b6f8436bSMarko Zec struct lps_walk_state wa;
783b8598e2fSAlexander V. Chernikov struct timespec ts_pre, ts_post;
784b6f8436bSMarko Zec struct nhop_object *nh_fib;
785b8598e2fSAlexander V. Chernikov uint64_t total_diff, lps;
7864a77a9b6SAlexander V. Chernikov uint32_t *keys, fibnum;
787b6f8436bSMarko Zec uint32_t t, p;
788a43104ebSMarko Zec uintptr_t acc = 0;
789b6f8436bSMarko Zec int i, pos, count = 0;
790b6f8436bSMarko Zec int seq = 0, rep = 0;
791a43104ebSMarko Zec int error;
792b8598e2fSAlexander V. Chernikov
793b8598e2fSAlexander V. Chernikov error = sysctl_handle_int(oidp, &count, 0, req);
794b8598e2fSAlexander V. Chernikov if (error != 0)
795b8598e2fSAlexander V. Chernikov return (error);
796b8598e2fSAlexander V. Chernikov if (count <= 0)
797b8598e2fSAlexander V. Chernikov return (0);
7984a77a9b6SAlexander V. Chernikov fibnum = curthread->td_proc->p_fibnum;
799b8598e2fSAlexander V. Chernikov
800b8598e2fSAlexander V. Chernikov keys = malloc(sizeof(*keys) * count, M_TEMP, M_NOWAIT);
801a43104ebSMarko Zec if (keys == NULL)
802b8598e2fSAlexander V. Chernikov return (ENOMEM);
803b8598e2fSAlexander V. Chernikov printf("Preparing %d random keys...\n", count);
804b8598e2fSAlexander V. Chernikov arc4random_buf(keys, sizeof(*keys) * count);
805b6f8436bSMarko Zec if (arg2 & LPS_ANN) {
806b6f8436bSMarko Zec wa.keys = keys;
807b6f8436bSMarko Zec wa.pos = 0;
808b6f8436bSMarko Zec wa.lim = count;
809b6f8436bSMarko Zec printf("Reducing keys to announced address space...\n");
810b6f8436bSMarko Zec do {
8114a77a9b6SAlexander V. Chernikov rib_walk(fibnum, AF_INET, false, reduce_keys,
812b6f8436bSMarko Zec &wa);
813b6f8436bSMarko Zec } while (wa.pos < wa.lim);
814b6f8436bSMarko Zec printf("Reshuffling keys...\n");
815b6f8436bSMarko Zec for (int i = 0; i < count; i++) {
816b6f8436bSMarko Zec p = random() % count;
817b6f8436bSMarko Zec t = keys[p];
818b6f8436bSMarko Zec keys[p] = keys[i];
819b6f8436bSMarko Zec keys[i] = t;
820b6f8436bSMarko Zec }
821b6f8436bSMarko Zec }
822b6f8436bSMarko Zec
823b6f8436bSMarko Zec if (arg2 & LPS_REP) {
824b6f8436bSMarko Zec rep = 1;
825b6f8436bSMarko Zec printf("REP ");
826b6f8436bSMarko Zec }
827b6f8436bSMarko Zec if (arg2 & LPS_SEQ) {
828b6f8436bSMarko Zec seq = 1;
829b6f8436bSMarko Zec printf("SEQ");
830b6f8436bSMarko Zec } else if (arg2 & LPS_ANN)
831b6f8436bSMarko Zec printf("ANN");
832b6f8436bSMarko Zec else
833b6f8436bSMarko Zec printf("RND");
834b6f8436bSMarko Zec printf(" LPS test starting...\n");
835b8598e2fSAlexander V. Chernikov
836b8598e2fSAlexander V. Chernikov NET_EPOCH_ENTER(et);
837b8598e2fSAlexander V. Chernikov nanouptime(&ts_pre);
838b6f8436bSMarko Zec for (i = 0, pos = 0; i < count; i++) {
839b6f8436bSMarko Zec key.s_addr = keys[pos++] ^ ((acc >> 10) & 0xff);
8404a77a9b6SAlexander V. Chernikov nh_fib = fib4_lookup(fibnum, key, 0, NHR_NONE, 0);
841b6f8436bSMarko Zec if (seq) {
842b6f8436bSMarko Zec if (nh_fib != NULL) {
843b6f8436bSMarko Zec acc += (uintptr_t) nh_fib + 123;
844b6f8436bSMarko Zec if (acc & 0x1000)
845b6f8436bSMarko Zec acc += (uintptr_t) nh_fib->nh_ifp;
846b6f8436bSMarko Zec else
847b6f8436bSMarko Zec acc -= (uintptr_t) nh_fib->nh_ifp;
848b6f8436bSMarko Zec } else
849b6f8436bSMarko Zec acc ^= (acc >> 3) + (acc << 2) + i;
850b6f8436bSMarko Zec if (acc & 0x800)
851b6f8436bSMarko Zec pos++;
852b6f8436bSMarko Zec if (pos >= count)
853b6f8436bSMarko Zec pos = 0;
854a43104ebSMarko Zec }
855b6f8436bSMarko Zec if (rep && ((i & 0xf) == 0xf)) {
856b6f8436bSMarko Zec pos -= 0xf;
857b6f8436bSMarko Zec if (pos < 0)
858b6f8436bSMarko Zec pos += 0xf;
859a43104ebSMarko Zec }
860a43104ebSMarko Zec }
861b8598e2fSAlexander V. Chernikov nanouptime(&ts_post);
862b8598e2fSAlexander V. Chernikov NET_EPOCH_EXIT(et);
863b8598e2fSAlexander V. Chernikov
864b8598e2fSAlexander V. Chernikov free(keys, M_TEMP);
865b8598e2fSAlexander V. Chernikov
866b8598e2fSAlexander V. Chernikov total_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
867b6f8436bSMarko Zec (ts_post.tv_nsec - ts_pre.tv_nsec);
868b8598e2fSAlexander V. Chernikov lps = 1000000000ULL * count / total_diff;
869b6f8436bSMarko Zec printf("%d lookups in %zu.%06zu milliseconds, %lu.%06lu MLPS\n",
870b6f8436bSMarko Zec count, total_diff / 1000000, total_diff % 1000000,
871b6f8436bSMarko Zec lps / 1000000, lps % 1000000);
872b8598e2fSAlexander V. Chernikov
873b8598e2fSAlexander V. Chernikov return (0);
874b8598e2fSAlexander V. Chernikov }
875a43104ebSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_rnd,
876a43104ebSMarko Zec CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
877b6f8436bSMarko Zec 0, 0, rnd_lps, "I",
878a43104ebSMarko Zec "Measure lookups per second, uniformly random keys, independent lookups");
879b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_rnd_ann,
880b6f8436bSMarko Zec CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
881b6f8436bSMarko Zec 0, LPS_ANN, rnd_lps, "I",
882b6f8436bSMarko Zec "Measure lookups per second, random keys from announced address space, "
883b6f8436bSMarko Zec "independent lookups");
884b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_seq,
885b6f8436bSMarko Zec CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
886b6f8436bSMarko Zec 0, LPS_SEQ, rnd_lps, "I",
887b6f8436bSMarko Zec "Measure lookups per second, uniformly random keys, "
888b6f8436bSMarko Zec "artificial dependencies between lookups");
889b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_seq_ann,
890b6f8436bSMarko Zec CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
891b6f8436bSMarko Zec 0, LPS_SEQ | LPS_ANN, rnd_lps, "I",
892b6f8436bSMarko Zec "Measure lookups per second, random keys from announced address space, "
893b6f8436bSMarko Zec "artificial dependencies between lookups");
894b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_rnd_rep,
895b6f8436bSMarko Zec CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
896b6f8436bSMarko Zec 0, LPS_REP, rnd_lps, "I",
897b6f8436bSMarko Zec "Measure lookups per second, uniformly random keys, independent lookups, "
898b6f8436bSMarko Zec "repeated keys");
899b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_rnd_ann_rep,
900b6f8436bSMarko Zec CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
901b6f8436bSMarko Zec 0, LPS_ANN | LPS_REP, rnd_lps, "I",
902b6f8436bSMarko Zec "Measure lookups per second, random keys from announced address space, "
903b6f8436bSMarko Zec "independent lookups, repeated keys");
904b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_seq_rep,
905b6f8436bSMarko Zec CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
906b6f8436bSMarko Zec 0, LPS_SEQ | LPS_REP, rnd_lps, "I",
907b6f8436bSMarko Zec "Measure lookups per second, uniformly random keys, "
908b6f8436bSMarko Zec "artificial dependencies between lookups, repeated keys");
909b6f8436bSMarko Zec SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_seq_ann_rep,
910b6f8436bSMarko Zec CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
911b6f8436bSMarko Zec 0, LPS_SEQ | LPS_ANN | LPS_REP, rnd_lps, "I",
912b6f8436bSMarko Zec "Measure lookups per second, random keys from announced address space, "
913b6f8436bSMarko Zec "artificial dependencies between lookups, repeated keys");
9140433870eSAlexander V. Chernikov
9150433870eSAlexander V. Chernikov static int
test_fib_lookup_modevent(module_t mod,int type,void * unused)9160433870eSAlexander V. Chernikov test_fib_lookup_modevent(module_t mod, int type, void *unused)
9170433870eSAlexander V. Chernikov {
9180433870eSAlexander V. Chernikov int error = 0;
9190433870eSAlexander V. Chernikov
9200433870eSAlexander V. Chernikov switch (type) {
9210433870eSAlexander V. Chernikov case MOD_LOAD:
9220433870eSAlexander V. Chernikov break;
9230433870eSAlexander V. Chernikov case MOD_UNLOAD:
9240433870eSAlexander V. Chernikov if (V_inet_addr_list != NULL)
9250433870eSAlexander V. Chernikov free(V_inet_addr_list, M_TEMP);
9260433870eSAlexander V. Chernikov if (V_inet6_addr_list != NULL)
9270433870eSAlexander V. Chernikov free(V_inet6_addr_list, M_TEMP);
9280433870eSAlexander V. Chernikov break;
9290433870eSAlexander V. Chernikov default:
9300433870eSAlexander V. Chernikov error = EOPNOTSUPP;
9310433870eSAlexander V. Chernikov break;
9320433870eSAlexander V. Chernikov }
9330433870eSAlexander V. Chernikov return (error);
9340433870eSAlexander V. Chernikov }
9350433870eSAlexander V. Chernikov
9360433870eSAlexander V. Chernikov static moduledata_t testfiblookupmod = {
9370433870eSAlexander V. Chernikov "test_fib_lookup",
9380433870eSAlexander V. Chernikov test_fib_lookup_modevent,
9390433870eSAlexander V. Chernikov 0
9400433870eSAlexander V. Chernikov };
9410433870eSAlexander V. Chernikov
9420433870eSAlexander V. Chernikov DECLARE_MODULE(testfiblookupmod, testfiblookupmod, SI_SUB_PSEUDO, SI_ORDER_ANY);
9430433870eSAlexander V. Chernikov MODULE_VERSION(testfiblookup, 1);
944