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);
Cache object: 3d42b01195f2659f1db77b37e8fb13a3
|