xref: /freebsd/sys/tests/fib_lookup/fib_lookup.c (revision 58d84ef87094691bf1ad9608964ec85f120cd34a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2020 Alexander V. Chernikov
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 #include "opt_inet.h"
31 #include "opt_inet6.h"
32 
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/lock.h>
36 #include <sys/rmlock.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
39 #include <sys/kernel.h>
40 #include <sys/socket.h>
41 #include <sys/sysctl.h>
42 #include <net/vnet.h>
43 
44 #include <net/if.h>
45 #include <net/if_var.h>
46 
47 #include <netinet/in.h>
48 #include <netinet/in_fib.h>
49 #include <netinet/ip.h>
50 
51 #include <netinet6/in6_fib.h>
52 
53 #include <net/route.h>
54 #include <net/route/nhop.h>
55 #include <net/route/route_ctl.h>
56 #include <net/route/route_var.h>
57 #include <net/route/fib_algo.h>
58 
59 #define	CHUNK_SIZE	10000
60 
61 VNET_DEFINE_STATIC(struct in_addr *, inet_addr_list);
62 #define	V_inet_addr_list	VNET(inet_addr_list)
63 VNET_DEFINE_STATIC(int, inet_list_size);
64 #define	V_inet_list_size	VNET(inet_list_size)
65 
66 VNET_DEFINE_STATIC(struct in6_addr *, inet6_addr_list);
67 #define	V_inet6_addr_list	VNET(inet6_addr_list)
68 VNET_DEFINE_STATIC(int, inet6_list_size);
69 #define	V_inet6_list_size	VNET(inet6_list_size)
70 
71 SYSCTL_DECL(_net_route);
72 SYSCTL_NODE(_net_route, OID_AUTO, test, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
73     "Route algorithm lookups");
74 
75 static int
76 add_addr(int family, char *addr_str)
77 {
78 
79 	if (family == AF_INET) {
80 		struct in_addr *paddr_old = V_inet_addr_list;
81 		int size_old = V_inet_list_size;
82 		struct in_addr addr;
83 
84 		if (inet_pton(AF_INET, addr_str, &addr) != 1)
85 			return (EINVAL);
86 
87 		struct in_addr *paddr = mallocarray(size_old + 1,
88 		    sizeof(struct in_addr), M_TEMP, M_ZERO | M_WAITOK);
89 
90 		if (paddr_old != NULL) {
91 			memcpy(paddr, paddr_old, size_old * sizeof(struct in_addr));
92 			free(paddr_old, M_TEMP);
93 		}
94 		paddr[size_old] = addr;
95 
96 		V_inet_addr_list = paddr;
97 		V_inet_list_size = size_old + 1;
98 		inet_ntop(AF_INET, &addr, addr_str, sizeof(addr_str));
99 	} else if (family == AF_INET6) {
100 		struct in6_addr *paddr_old = V_inet6_addr_list;
101 		int size_old = V_inet6_list_size;
102 		struct in6_addr addr6;
103 
104 		if (inet_pton(AF_INET6, addr_str, &addr6) != 1)
105 			return (EINVAL);
106 
107 		struct in6_addr *paddr = mallocarray(size_old + 1,
108 		    sizeof(struct in6_addr), M_TEMP, M_ZERO | M_WAITOK);
109 
110 		if (paddr_old != NULL) {
111 			memcpy(paddr, paddr_old, size_old * sizeof(struct in6_addr));
112 			free(paddr_old, M_TEMP);
113 		}
114 		paddr[size_old] = addr6;
115 
116 		V_inet6_addr_list = paddr;
117 		V_inet6_list_size = size_old + 1;
118 		inet_ntop(AF_INET6, &addr6, addr_str, sizeof(addr_str));
119 	}
120 
121 	return (0);
122 }
123 
124 static int
125 add_addr_sysctl_handler(struct sysctl_oid *oidp, struct sysctl_req *req, int family)
126 {
127 	char addr_str[INET6_ADDRSTRLEN];
128 	int error;
129 
130 	bzero(addr_str, sizeof(addr_str));
131 
132 	error = sysctl_handle_string(oidp, addr_str, sizeof(addr_str), req);
133 	if (error != 0 || req->newptr == NULL)
134 		return (error);
135 
136 	error = add_addr(family, addr_str);
137 
138 	return (0);
139 }
140 
141 static int
142 add_inet_addr_sysctl_handler(SYSCTL_HANDLER_ARGS)
143 {
144 
145 	return (add_addr_sysctl_handler(oidp, req, AF_INET));
146 }
147 SYSCTL_PROC(_net_route_test, OID_AUTO, add_inet_addr,
148     CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
149     add_inet_addr_sysctl_handler, "A", "Set");
150 
151 static int
152 add_inet6_addr_sysctl_handler(SYSCTL_HANDLER_ARGS)
153 {
154 
155 	return (add_addr_sysctl_handler(oidp, req, AF_INET6));
156 }
157 SYSCTL_PROC(_net_route_test, OID_AUTO, add_inet6_addr,
158     CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
159     add_inet6_addr_sysctl_handler, "A", "Set");
160 
161 static uint64_t
162 run_test_inet_one_pass(uint32_t fibnum)
163 {
164 	/* Assume epoch */
165 	int sz = V_inet_list_size;
166 	int tries = CHUNK_SIZE / sz;
167 	const struct in_addr *a = V_inet_addr_list;
168 	uint64_t count = 0;
169 
170 	for (int pass = 0; pass < tries; pass++) {
171 		for (int i = 0; i < sz; i++) {
172 			fib4_lookup(fibnum, a[i], 0, NHR_NONE, 0);
173 			count++;
174 		}
175 	}
176 	return (count);
177 }
178 
179 static int
180 run_test_inet(SYSCTL_HANDLER_ARGS)
181 {
182 	struct epoch_tracker et;
183 
184 	int count = 0;
185 	int error = sysctl_handle_int(oidp, &count, 0, req);
186 	if (error != 0)
187 		return (error);
188 
189 	if (count == 0)
190 		return (0);
191 
192 	if (V_inet_list_size <= 0)
193 		return (ENOENT);
194 
195 	printf("run: %d packets vnet %p\n", count, curvnet);
196 	if (count < CHUNK_SIZE)
197 		count = CHUNK_SIZE;
198 
199 	struct timespec ts_pre, ts_post;
200 	int64_t pass_diff, total_diff = 0;
201 	uint64_t pass_packets, total_packets = 0;
202 	uint32_t fibnum = curthread->td_proc->p_fibnum;
203 
204 	for (int pass = 0; pass < count / CHUNK_SIZE; pass++) {
205 		NET_EPOCH_ENTER(et);
206 		nanouptime(&ts_pre);
207 		pass_packets = run_test_inet_one_pass(fibnum);
208 		nanouptime(&ts_post);
209 		NET_EPOCH_EXIT(et);
210 
211 		pass_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
212 		    (ts_post.tv_nsec - ts_pre.tv_nsec);
213 		total_diff += pass_diff;
214 		total_packets += pass_packets;
215 	}
216 
217 	printf("%zu packets in %zu nanoseconds, %zu pps\n",
218 	    total_packets, total_diff, total_packets * 1000000000 / total_diff);
219 
220 	return (0);
221 }
222 SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet,
223     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
224     0, 0, run_test_inet, "I", "Execute fib4_lookup test");
225 
226 static uint64_t
227 run_test_inet6_one_pass(uint32_t fibnum)
228 {
229 	/* Assume epoch */
230 	int sz = V_inet6_list_size;
231 	int tries = CHUNK_SIZE / sz;
232 	const struct in6_addr *a = V_inet6_addr_list;
233 	uint64_t count = 0;
234 
235 	for (int pass = 0; pass < tries; pass++) {
236 		for (int i = 0; i < sz; i++) {
237 			fib6_lookup(fibnum, &a[i], 0, NHR_NONE, 0);
238 			count++;
239 		}
240 	}
241 	return (count);
242 }
243 
244 static int
245 run_test_inet6(SYSCTL_HANDLER_ARGS)
246 {
247 	struct epoch_tracker et;
248 
249 	int count = 0;
250 	int error = sysctl_handle_int(oidp, &count, 0, req);
251 	if (error != 0)
252 		return (error);
253 
254 	if (count == 0)
255 		return (0);
256 
257 	if (V_inet6_list_size <= 0)
258 		return (ENOENT);
259 
260 	printf("run: %d packets vnet %p\n", count, curvnet);
261 	if (count < CHUNK_SIZE)
262 		count = CHUNK_SIZE;
263 
264 	struct timespec ts_pre, ts_post;
265 	int64_t pass_diff, total_diff = 0;
266 	uint64_t pass_packets, total_packets = 0;
267 	uint32_t fibnum = curthread->td_proc->p_fibnum;
268 
269 	for (int pass = 0; pass < count / CHUNK_SIZE; pass++) {
270 		NET_EPOCH_ENTER(et);
271 		nanouptime(&ts_pre);
272 		pass_packets = run_test_inet6_one_pass(fibnum);
273 		nanouptime(&ts_post);
274 		NET_EPOCH_EXIT(et);
275 
276 		pass_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
277 		    (ts_post.tv_nsec - ts_pre.tv_nsec);
278 		total_diff += pass_diff;
279 		total_packets += pass_packets;
280 	}
281 
282 	printf("%zu packets in %zu nanoseconds, %zu pps\n",
283 	    total_packets, total_diff, total_packets * 1000000000 / total_diff);
284 
285 	return (0);
286 }
287 SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet6,
288     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
289     0, 0, run_test_inet6, "I", "Execute fib6_lookup test");
290 
291 static bool
292 cmp_dst(uint32_t fibnum, struct in_addr a)
293 {
294 	struct nhop_object *nh_fib;
295 	struct rtentry *rt;
296 	struct route_nhop_data rnd = {};
297 
298 	nh_fib = fib4_lookup(fibnum, a, 0, NHR_NONE, 0);
299 	rt = fib4_lookup_rt(fibnum, a, 0, NHR_NONE, &rnd);
300 
301 	if (nh_fib == NULL && rt == NULL) {
302 		return (true);
303 	} else if (nh_fib == nhop_select(rnd.rnd_nhop, 0)) {
304 		return (true);
305 	}
306 
307 	struct in_addr dst;
308 	int plen;
309 	uint32_t scopeid;
310 	char key_str[INET_ADDRSTRLEN], dst_str[INET_ADDRSTRLEN];
311 
312 	inet_ntop(AF_INET, &a, key_str, sizeof(key_str));
313 	if (rnd.rnd_nhop == NULL) {
314 		printf("[RT BUG] lookup for %s: RIB: ENOENT FIB: nh=%u\n",
315 		    key_str, nhop_get_idx(nh_fib));
316 	} else {
317 		rt_get_inet_prefix_plen(rt, &dst, &plen, &scopeid);
318 		inet_ntop(AF_INET, &dst, dst_str, sizeof(dst_str));
319 		printf("[RT BUG] lookup for %s: RIB: %s/%d,nh=%u FIB: nh=%u\n",
320 		    key_str, dst_str, plen,
321 		    nhop_get_idx(nhop_select(rnd.rnd_nhop, 0)),
322 		    nh_fib ? nhop_get_idx(nh_fib) : 0);
323 	}
324 
325 	return (false);
326 }
327 
328 static bool
329 cmp_dst6(uint32_t fibnum, const struct in6_addr *a)
330 {
331 	struct nhop_object *nh_fib;
332 	struct rtentry *rt;
333 	struct route_nhop_data rnd = {};
334 
335 	nh_fib = fib6_lookup(fibnum, a, 0, NHR_NONE, 0);
336 	rt = fib6_lookup_rt(fibnum, a, 0, NHR_NONE, &rnd);
337 
338 	if (nh_fib == NULL && rt == NULL) {
339 		return (true);
340 	} else if (nh_fib == nhop_select(rnd.rnd_nhop, 0)) {
341 		return (true);
342 	}
343 
344 	struct in6_addr dst;
345 	int plen;
346 	uint32_t scopeid;
347 	char key_str[INET6_ADDRSTRLEN], dst_str[INET6_ADDRSTRLEN];
348 
349 	inet_ntop(AF_INET6, a, key_str, sizeof(key_str));
350 	if (rnd.rnd_nhop == NULL) {
351 		printf("[RT BUG] lookup for %s: RIB: ENOENT FIB: nh=%u\n",
352 		    key_str, nhop_get_idx(nh_fib));
353 	} else {
354 		rt_get_inet6_prefix_plen(rt, &dst, &plen, &scopeid);
355 		inet_ntop(AF_INET6, &dst, dst_str, sizeof(dst_str));
356 		printf("[RT BUG] lookup for %s: RIB: %s/%d,nh=%u FIB: nh=%u\n",
357 		    key_str, dst_str, plen,
358 		    nhop_get_idx(nhop_select(rnd.rnd_nhop, 0)),
359 		    nh_fib ? nhop_get_idx(nh_fib) : 0);
360 	}
361 
362 	return (false);
363 }
364 
365 /* Random lookups: correctness verification */
366 static uint64_t
367 run_test_inet_one_pass_random(uint32_t fibnum)
368 {
369 	/* Assume epoch */
370 	struct in_addr a[64];
371 	int sz = 64;
372 	uint64_t count = 0;
373 
374 	for (int pass = 0; pass < CHUNK_SIZE / sz; pass++) {
375 		arc4random_buf(a, sizeof(a));
376 		for (int i = 0; i < sz; i++) {
377 			if (!cmp_dst(fibnum, a[i]))
378 				return (0);
379 			count++;
380 		}
381 	}
382 	return (count);
383 }
384 
385 static int
386 run_test_inet_random(SYSCTL_HANDLER_ARGS)
387 {
388 	struct epoch_tracker et;
389 
390 	int count = 0;
391 	int error = sysctl_handle_int(oidp, &count, 0, req);
392 	if (error != 0)
393 		return (error);
394 
395 	if (count == 0)
396 		return (0);
397 
398 	if (count < CHUNK_SIZE)
399 		count = CHUNK_SIZE;
400 
401 	struct timespec ts_pre, ts_post;
402 	int64_t pass_diff, total_diff = 1;
403 	uint64_t pass_packets, total_packets = 0;
404 	uint32_t fibnum = curthread->td_proc->p_fibnum;
405 
406 	for (int pass = 0; pass < count / CHUNK_SIZE; pass++) {
407 		NET_EPOCH_ENTER(et);
408 		nanouptime(&ts_pre);
409 		pass_packets = run_test_inet_one_pass_random(fibnum);
410 		nanouptime(&ts_post);
411 		NET_EPOCH_EXIT(et);
412 
413 		pass_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
414 		    (ts_post.tv_nsec - ts_pre.tv_nsec);
415 		total_diff += pass_diff;
416 		total_packets += pass_packets;
417 
418 		if (pass_packets == 0)
419 			break;
420 	}
421 
422 	/* Signal error to userland */
423 	if (pass_packets == 0)
424 		return (EINVAL);
425 
426 	printf("%zu packets in %zu nanoseconds, %zu pps\n",
427 	    total_packets, total_diff, total_packets * 1000000000 / total_diff);
428 
429 	return (0);
430 }
431 SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet_random,
432     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
433     0, 0, run_test_inet_random, "I", "Execute fib4_lookup random check tests");
434 
435 
436 struct inet_array {
437 	uint32_t alloc_items;
438 	uint32_t num_items;
439 	uint32_t rnh_prefixes;
440 	int error;
441 	struct in_addr *arr;
442 };
443 
444 /*
445  * For each prefix, add the following records to the lookup array:
446  * * prefix-1, prefix, prefix + 1, prefix_end, prefix_end + 1
447  */
448 static int
449 add_prefix(struct rtentry *rt, void *_data)
450 {
451 	struct inet_array *pa = (struct inet_array *)_data;
452 	struct in_addr addr;
453 	int plen;
454 	uint32_t scopeid, haddr;
455 
456 	pa->rnh_prefixes++;
457 
458 	if (pa->num_items + 5 >= pa->alloc_items) {
459 		if (pa->error == 0)
460 			pa->error = ENOSPC;
461 		return (0);
462 	}
463 
464 	rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid);
465 
466 	pa->arr[pa->num_items++] = addr;
467 	haddr = ntohl(addr.s_addr);
468 	if (haddr > 0) {
469 		pa->arr[pa->num_items++].s_addr = htonl(haddr - 1);
470 		pa->arr[pa->num_items++].s_addr = htonl(haddr + 1);
471 		/* assume mask != 0 */
472 		uint32_t mlen = (1 << (32 - plen)) - 1;
473 		pa->arr[pa->num_items++].s_addr = htonl(haddr + mlen);
474 		/* can overflow, but who cares */
475 		pa->arr[pa->num_items++].s_addr = htonl(haddr + mlen + 1);
476 	}
477 
478 	return (0);
479 }
480 
481 static bool
482 prepare_list(uint32_t fibnum, struct inet_array *pa)
483 {
484 	struct rib_head *rh;
485 
486 	rh = rt_tables_get_rnh(fibnum, AF_INET);
487 
488 	uint32_t num_prefixes = rh->rnh_prefixes;
489 	bzero(pa, sizeof(struct inet_array));
490 	pa->alloc_items = (num_prefixes + 10) * 5;
491 	pa->arr = mallocarray(pa->alloc_items, sizeof(struct in_addr),
492 	    M_TEMP, M_ZERO | M_WAITOK);
493 
494 	rib_walk(fibnum, AF_INET, false, add_prefix, pa);
495 
496 	if (pa->error != 0) {
497 		printf("prefixes: old: %u, current: %u, walked: %u, allocated: %u\n",
498 		    num_prefixes, rh->rnh_prefixes, pa->rnh_prefixes, pa->alloc_items);
499 	}
500 
501 	return (pa->error == 0);
502 }
503 
504 static int
505 run_test_inet_scan(SYSCTL_HANDLER_ARGS)
506 {
507 	struct epoch_tracker et;
508 
509 	int count = 0;
510 	int error = sysctl_handle_int(oidp, &count, 0, req);
511 	if (error != 0)
512 		return (error);
513 
514 	if (count == 0)
515 		return (0);
516 
517 	struct inet_array pa = {};
518 	uint32_t fibnum = curthread->td_proc->p_fibnum;
519 
520 	if (!prepare_list(fibnum, &pa))
521 		return (pa.error);
522 
523 	struct timespec ts_pre, ts_post;
524 	int64_t total_diff = 1;
525 	uint64_t total_packets = 0;
526 	int failure_count = 0;
527 
528 	NET_EPOCH_ENTER(et);
529 	nanouptime(&ts_pre);
530 	for (int i = 0; i < pa.num_items; i++) {
531 		if (!cmp_dst(fibnum, pa.arr[i])) {
532 			failure_count++;
533 		}
534 		total_packets++;
535 	}
536 	nanouptime(&ts_post);
537 	NET_EPOCH_EXIT(et);
538 
539 	if (pa.arr != NULL)
540 		free(pa.arr, M_TEMP);
541 
542 	/* Signal error to userland */
543 	if (failure_count > 0) {
544 		printf("[RT ERROR] total failures: %d\n", failure_count);
545 		return (EINVAL);
546 	}
547 
548 	total_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
549 	    (ts_post.tv_nsec - ts_pre.tv_nsec);
550 	printf("%zu packets in %zu nanoseconds, %zu pps\n",
551 	    total_packets, total_diff, total_packets * 1000000000 / total_diff);
552 
553 	return (0);
554 }
555 SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet_scan,
556     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
557     0, 0, run_test_inet_scan, "I", "Execute fib4_lookup scan tests");
558 
559 struct inet6_array {
560 	uint32_t alloc_items;
561 	uint32_t num_items;
562 	uint32_t rnh_prefixes;
563 	int error;
564 	struct in6_addr *arr;
565 };
566 
567 static bool
568 safe_add(uint32_t *v, uint32_t inc)
569 {
570 	if (*v < (UINT32_MAX - inc)) {
571 		*v += inc;
572 		return (true);
573 	} else {
574 		*v -= (UINT32_MAX - inc + 1);
575 		return (false);
576 	}
577 }
578 
579 static bool
580 safe_dec(uint32_t *v, uint32_t inc)
581 {
582 	if (*v >= inc) {
583 		*v -= inc;
584 		return (true);
585 	} else {
586 		*v += (UINT32_MAX - inc + 1);
587 		return (false);
588 	}
589 }
590 
591 static void
592 inc_prefix6(struct in6_addr *addr, int inc)
593 {
594 	for (int i = 0; i < 4; i++) {
595 		uint32_t v = ntohl(addr->s6_addr32[3 - i]);
596 		bool ret = safe_add(&v, inc);
597 		addr->s6_addr32[3 - i] = htonl(v);
598 		if (ret)
599 			return;
600 		inc = 1;
601 	}
602 }
603 
604 static void
605 dec_prefix6(struct in6_addr *addr, int dec)
606 {
607 	for (int i = 0; i < 4; i++) {
608 		uint32_t v = ntohl(addr->s6_addr32[3 - i]);
609 		bool ret = safe_dec(&v, dec);
610 		addr->s6_addr32[3 - i] = htonl(v);
611 		if (ret)
612 			return;
613 		dec = 1;
614 	}
615 }
616 
617 static void
618 ipv6_writemask(struct in6_addr *addr6, uint8_t mask)
619 {
620 	uint32_t *cp;
621 
622 	for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
623 		*cp++ = 0xFFFFFFFF;
624 	if (mask > 0)
625 		*cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
626 }
627 
628 /*
629  * For each prefix, add the following records to the lookup array:
630  * * prefix-1, prefix, prefix + 1, prefix_end, prefix_end + 1
631  */
632 static int
633 add_prefix6(struct rtentry *rt, void *_data)
634 {
635 	struct inet6_array *pa = (struct inet6_array *)_data;
636 	struct in6_addr addr, naddr;
637 	int plen;
638 	uint32_t scopeid;
639 
640 	pa->rnh_prefixes++;
641 
642 	if (pa->num_items + 5 >= pa->alloc_items) {
643 		if (pa->error == 0)
644 			pa->error = ENOSPC;
645 		return (0);
646 	}
647 
648 	rt_get_inet6_prefix_plen(rt, &addr, &plen, &scopeid);
649 
650 	pa->arr[pa->num_items++] = addr;
651 	if (!IN6_ARE_ADDR_EQUAL(&addr, &in6addr_any)) {
652 		naddr = addr;
653 		dec_prefix6(&naddr, 1);
654 		pa->arr[pa->num_items++] = naddr;
655 		naddr = addr;
656 		inc_prefix6(&naddr, 1);
657 		pa->arr[pa->num_items++] = naddr;
658 
659 		/* assume mask != 0 */
660 		struct in6_addr mask6;
661 		ipv6_writemask(&mask6, plen);
662 		naddr = addr;
663 		for (int i = 0; i < 3; i++)
664 			naddr.s6_addr32[i] = htonl(ntohl(naddr.s6_addr32[i]) | ~ntohl(mask6.s6_addr32[i]));
665 
666 		pa->arr[pa->num_items++] = naddr;
667 		inc_prefix6(&naddr, 1);
668 		pa->arr[pa->num_items++] = naddr;
669 	}
670 
671 	return (0);
672 }
673 
674 static bool
675 prepare_list6(uint32_t fibnum, struct inet6_array *pa)
676 {
677 	struct rib_head *rh;
678 
679 	rh = rt_tables_get_rnh(fibnum, AF_INET6);
680 
681 	uint32_t num_prefixes = rh->rnh_prefixes;
682 	bzero(pa, sizeof(struct inet6_array));
683 	pa->alloc_items = (num_prefixes + 10) * 5;
684 	pa->arr = mallocarray(pa->alloc_items, sizeof(struct in6_addr),
685 	    M_TEMP, M_ZERO | M_WAITOK);
686 
687 	rib_walk(fibnum, AF_INET6, false, add_prefix6, pa);
688 
689 	if (pa->error != 0) {
690 		printf("prefixes: old: %u, current: %u, walked: %u, allocated: %u\n",
691 		    num_prefixes, rh->rnh_prefixes, pa->rnh_prefixes, pa->alloc_items);
692 	}
693 
694 	return (pa->error == 0);
695 }
696 
697 static int
698 run_test_inet6_scan(SYSCTL_HANDLER_ARGS)
699 {
700 	struct epoch_tracker et;
701 
702 	int count = 0;
703 	int error = sysctl_handle_int(oidp, &count, 0, req);
704 	if (error != 0)
705 		return (error);
706 
707 	if (count == 0)
708 		return (0);
709 
710 	struct inet6_array pa = {};
711 	uint32_t fibnum = curthread->td_proc->p_fibnum;
712 
713 	if (!prepare_list6(fibnum, &pa))
714 		return (pa.error);
715 
716 	struct timespec ts_pre, ts_post;
717 	int64_t total_diff = 1;
718 	uint64_t total_packets = 0;
719 	int failure_count = 0;
720 
721 	NET_EPOCH_ENTER(et);
722 	nanouptime(&ts_pre);
723 	for (int i = 0; i < pa.num_items; i++) {
724 		if (!cmp_dst6(fibnum, &pa.arr[i])) {
725 			failure_count++;
726 		}
727 		total_packets++;
728 	}
729 	nanouptime(&ts_post);
730 	NET_EPOCH_EXIT(et);
731 
732 	if (pa.arr != NULL)
733 		free(pa.arr, M_TEMP);
734 
735 	/* Signal error to userland */
736 	if (failure_count > 0) {
737 		printf("[RT ERROR] total failures: %d\n", failure_count);
738 		return (EINVAL);
739 	}
740 
741 	total_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
742 	    (ts_post.tv_nsec - ts_pre.tv_nsec);
743 	printf("%zu packets in %zu nanoseconds, %zu pps\n",
744 	    total_packets, total_diff, total_packets * 1000000000 / total_diff);
745 
746 	return (0);
747 }
748 SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet6_scan,
749     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
750     0, 0, run_test_inet6_scan, "I", "Execute fib6_lookup scan tests");
751 
752 #define	LPS_SEQ		0x1
753 #define	LPS_ANN		0x2
754 #define	LPS_REP		0x4
755 
756 struct lps_walk_state {
757 	uint32_t *keys;
758 	int pos;
759 	int lim;
760 };
761 
762 static int
763 reduce_keys(struct rtentry *rt, void *_data)
764 {
765         struct lps_walk_state *wa = (struct lps_walk_state *) _data;
766 	struct in_addr addr;
767 	uint32_t scopeid;
768 	int plen;
769 
770 	rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid);
771 	wa->keys[wa->pos] = ntohl(addr.s_addr) |
772 	    (wa->keys[wa->pos] & ~(0xffffffffU << (32 - plen)));
773 
774 	wa->pos++;
775 	return (wa->pos == wa->lim);
776 }
777 
778 static int
779 rnd_lps(SYSCTL_HANDLER_ARGS)
780 {
781 	struct epoch_tracker et;
782 	struct in_addr key;
783 	struct lps_walk_state wa;
784 	struct timespec ts_pre, ts_post;
785 	struct nhop_object *nh_fib;
786 	uint64_t total_diff, lps;
787 	uint32_t *keys, fibnum;
788 	uint32_t t, p;
789 	uintptr_t acc = 0;
790 	int i, pos, count = 0;
791 	int seq = 0, rep = 0;
792 	int error;
793 
794 	error = sysctl_handle_int(oidp, &count, 0, req);
795 	if (error != 0)
796 		return (error);
797 	if (count <= 0)
798 		return (0);
799 	fibnum = curthread->td_proc->p_fibnum;
800 
801 	keys = malloc(sizeof(*keys) * count, M_TEMP, M_NOWAIT);
802 	if (keys == NULL)
803 		return (ENOMEM);
804 	printf("Preparing %d random keys...\n", count);
805 	arc4random_buf(keys, sizeof(*keys) * count);
806 	if (arg2 & LPS_ANN) {
807 		wa.keys = keys;
808 		wa.pos = 0;
809 		wa.lim = count;
810 		printf("Reducing keys to announced address space...\n");
811 		do {
812 			rib_walk(fibnum, AF_INET, false, reduce_keys,
813 			    &wa);
814 		} while (wa.pos < wa.lim);
815 		printf("Reshuffling keys...\n");
816 		for (int i = 0; i < count; i++) {
817 			p = random() % count;
818 			t = keys[p];
819 			keys[p] = keys[i];
820 			keys[i] = t;
821 		}
822 	}
823 
824 	if (arg2 & LPS_REP) {
825 		rep = 1;
826 		printf("REP ");
827 	}
828 	if (arg2 & LPS_SEQ) {
829 		seq = 1;
830 		printf("SEQ");
831 	} else if (arg2 & LPS_ANN)
832 		printf("ANN");
833 	else
834 		printf("RND");
835 	printf(" LPS test starting...\n");
836 
837 	NET_EPOCH_ENTER(et);
838 	nanouptime(&ts_pre);
839 	for (i = 0, pos = 0; i < count; i++) {
840 		key.s_addr = keys[pos++] ^ ((acc >> 10) & 0xff);
841 		nh_fib = fib4_lookup(fibnum, key, 0, NHR_NONE, 0);
842 		if (seq) {
843 			if (nh_fib != NULL) {
844 				acc += (uintptr_t) nh_fib + 123;
845 				if (acc & 0x1000)
846 					acc += (uintptr_t) nh_fib->nh_ifp;
847 				else
848 					acc -= (uintptr_t) nh_fib->nh_ifp;
849 			} else
850 				acc ^= (acc >> 3) + (acc << 2) + i;
851 			if (acc & 0x800)
852 				pos++;
853 			if (pos >= count)
854 				pos = 0;
855 		}
856 		if (rep && ((i & 0xf) == 0xf)) {
857 			pos -= 0xf;
858 			if (pos < 0)
859 				pos += 0xf;
860 		}
861 	}
862 	nanouptime(&ts_post);
863 	NET_EPOCH_EXIT(et);
864 
865 	free(keys, M_TEMP);
866 
867 	total_diff = (ts_post.tv_sec - ts_pre.tv_sec) * 1000000000 +
868 	    (ts_post.tv_nsec - ts_pre.tv_nsec);
869 	lps = 1000000000ULL * count / total_diff;
870 	printf("%d lookups in %zu.%06zu milliseconds, %lu.%06lu MLPS\n",
871 	    count, total_diff / 1000000, total_diff % 1000000,
872 	    lps / 1000000, lps % 1000000);
873 
874 	return (0);
875 }
876 SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_rnd,
877     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
878     0, 0, rnd_lps, "I",
879     "Measure lookups per second, uniformly random keys, independent lookups");
880 SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_rnd_ann,
881     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
882     0, LPS_ANN, rnd_lps, "I",
883     "Measure lookups per second, random keys from announced address space, "
884     "independent lookups");
885 SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_seq,
886     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
887     0, LPS_SEQ, rnd_lps, "I",
888     "Measure lookups per second, uniformly random keys, "
889     "artificial dependencies between lookups");
890 SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_seq_ann,
891     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
892     0, LPS_SEQ | LPS_ANN, rnd_lps, "I",
893     "Measure lookups per second, random keys from announced address space, "
894     "artificial dependencies between lookups");
895 SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_rnd_rep,
896     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
897     0, LPS_REP, rnd_lps, "I",
898     "Measure lookups per second, uniformly random keys, independent lookups, "
899     "repeated keys");
900 SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_rnd_ann_rep,
901     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
902     0, LPS_ANN | LPS_REP, rnd_lps, "I",
903     "Measure lookups per second, random keys from announced address space, "
904     "independent lookups, repeated keys");
905 SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_seq_rep,
906     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
907     0, LPS_SEQ | LPS_REP, rnd_lps, "I",
908     "Measure lookups per second, uniformly random keys, "
909     "artificial dependencies between lookups, repeated keys");
910 SYSCTL_PROC(_net_route_test, OID_AUTO, run_lps_seq_ann_rep,
911     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
912     0, LPS_SEQ | LPS_ANN | LPS_REP, rnd_lps, "I",
913     "Measure lookups per second, random keys from announced address space, "
914     "artificial dependencies between lookups, repeated keys");
915 
916 static int
917 test_fib_lookup_modevent(module_t mod, int type, void *unused)
918 {
919 	int error = 0;
920 
921 	switch (type) {
922 	case MOD_LOAD:
923 		break;
924 	case MOD_UNLOAD:
925 		if (V_inet_addr_list != NULL)
926 			free(V_inet_addr_list, M_TEMP);
927 		if (V_inet6_addr_list != NULL)
928 			free(V_inet6_addr_list, M_TEMP);
929 		break;
930 	default:
931 		error = EOPNOTSUPP;
932 		break;
933 	}
934 	return (error);
935 }
936 
937 static moduledata_t testfiblookupmod = {
938         "test_fib_lookup",
939         test_fib_lookup_modevent,
940         0
941 };
942 
943 DECLARE_MODULE(testfiblookupmod, testfiblookupmod, SI_SUB_PSEUDO, SI_ORDER_ANY);
944 MODULE_VERSION(testfiblookup, 1);
945