1/* $NetBSD: portalgo.c,v 1.10 2016/04/26 08:44:44 ozaki-r Exp $ */
2
3/*
4 * Copyright 2011 Vlad Balan
5 *
6 * Written by Vlad Balan for the NetBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 */
30
31/*
32 * see:
33 * RFC 6056 Recommendations for Transport-Protocol Port Randomization
34 */
35
36#include <sys/cdefs.h>
37__KERNEL_RCSID(0, "$NetBSD: portalgo.c,v 1.10 2016/04/26 08:44:44 ozaki-r Exp $");
38
39#ifdef _KERNEL_OPT
40#include "opt_inet.h"
41#endif
42
43#include <sys/param.h>
44#include <sys/errno.h>
45#include <sys/kauth.h>
46#include <sys/uidinfo.h>
47#include <sys/domain.h>
48#include <sys/md5.h>
49#include <sys/cprng.h>
50#include <sys/bitops.h>
51
52#include <net/if.h>
53
54#include <netinet/in.h>
55#include <netinet/in_systm.h>
56#include <netinet/ip.h>
57#include <netinet/in_pcb.h>
58#include <netinet/in_var.h>
59#include <netinet/ip_var.h>
60
61#ifdef INET6
62#include <netinet/ip6.h>
63#include <netinet6/ip6_var.h>
64#include <netinet6/in6_pcb.h>
65#endif
66
67#include <netinet/tcp_vtw.h>
68
69#include "portalgo.h"
70
71#define NPROTO 2
72#define PORTALGO_TCP 0
73#define PORTALGO_UDP 1
74
75#define NAF 2
76#define PORTALGO_IPV4 0
77#define PORTALGO_IPV6 1
78
79#define NRANGES 2
80#define PORTALGO_LOWPORT 0
81#define PORTALGO_HIGHPORT 1
82
83#if PORTALGO_DEBUG
84static bool portalgo_debug = true;
85#define DPRINTF if (portalgo_debug) printf
86#else
87#define DPRINTF while (/*CONSTCOND*/0) printf
88#endif
89
90#ifndef PORTALGO_INET4_DEFAULT
91#define PORTALGO_INET4_DEFAULT PORTALGO_BSD
92#endif
93#ifndef PORTALGO_INET6_DEFAULT
94#define PORTALGO_INET6_DEFAULT PORTALGO_BSD
95#endif
96
97typedef __BITMAP_TYPE(, uint32_t, 0x10000) bitmap;
98#ifdef INET
99static int inet4_portalgo = PORTALGO_INET4_DEFAULT;
100static bitmap inet4_reserve;
101#endif
102#ifdef INET6
103static int inet6_portalgo = PORTALGO_INET6_DEFAULT;
104static bitmap inet6_reserve;
105#endif
106
107typedef struct {
108 const char *name;
109 int (*func)(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
110} portalgo_algorithm_t;
111
112static int algo_bsd(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
113static int algo_random_start(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
114static int algo_random_pick(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
115static int algo_hash(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
116static int algo_doublehash(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
117static int algo_randinc(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
118
119static const portalgo_algorithm_t algos[] = {
120 {
121 .name = "bsd",
122 .func = algo_bsd
123 },
124 {
125 .name = "random_start",
126 .func = algo_random_start
127 },
128 {
129 .name = "random_pick",
130 .func = algo_random_pick
131 },
132 {
133 .name = "hash",
134 .func = algo_hash
135 },
136 {
137 .name = "doublehash",
138 .func = algo_doublehash
139 },
140 {
141 .name = "randinc",
142 .func = algo_randinc
143 }
144};
145
146#define NALGOS __arraycount(algos)
147
148static uint16_t portalgo_next_ephemeral[NPROTO][NAF][NRANGES][NALGOS];
149
150/*
151 * Access the pcb and copy the values of the last port and the ends of
152 * the port range.
153 */
154static int
155pcb_getports(struct inpcb_hdr *inp_hdr, uint16_t *lastport,
156 uint16_t *mymin, uint16_t *mymax, uint16_t **pnext_ephemeral, int algo)
157{
158 struct inpcbtable * const table = inp_hdr->inph_table;
159 struct socket *so;
160 int portalgo_proto;
161 int portalgo_af;
162 int portalgo_range;
163
164 so = inp_hdr->inph_socket;
165 switch (so->so_type) {
166 case SOCK_DGRAM: /* UDP or DCCP */
167 case SOCK_CONN_DGRAM:
168 portalgo_proto = PORTALGO_UDP;
169 break;
170 case SOCK_STREAM: /* TCP or SCTP */
171 portalgo_proto = PORTALGO_TCP;
172 break;
173 default:
174 return EPFNOSUPPORT;
175 }
176
177 switch (inp_hdr->inph_af) {
178#ifdef INET
179 case AF_INET: {
180 struct inpcb *inp = (struct inpcb *)(void *)inp_hdr;
181
182 portalgo_af = PORTALGO_IPV4;
183 if (inp->inp_flags & INP_LOWPORT) {
184 *mymin = lowportmin;
185 *mymax = lowportmax;
186 *lastport = table->inpt_lastlow;
187 portalgo_range = PORTALGO_LOWPORT;
188 } else {
189 *mymin = anonportmin;
190 *mymax = anonportmax;
191 *lastport = table->inpt_lastport;
192 portalgo_range = PORTALGO_HIGHPORT;
193 }
194 break;
195 }
196#endif
197#ifdef INET6
198 case AF_INET6: {
199 struct in6pcb *in6p = (struct in6pcb *)(void *)inp_hdr;
200
201 portalgo_af = PORTALGO_IPV6;
202 if (in6p->in6p_flags & IN6P_LOWPORT) {
203 *mymin = ip6_lowportmin;
204 *mymax = ip6_lowportmax;
205 *lastport = table->inpt_lastlow;
206 portalgo_range = PORTALGO_LOWPORT;
207 } else {
208 *mymin = ip6_anonportmin;
209 *mymax = ip6_anonportmax;
210 *lastport = table->inpt_lastport;
211 portalgo_range = PORTALGO_HIGHPORT;
212 }
213 break;
214 }
215#endif
216 default:
217 return EAFNOSUPPORT;
218 }
219
220 if (*mymin > *mymax) { /* sanity check */
221 u_int16_t swp;
222
223 swp = *mymin;
224 *mymin = *mymax;
225 *mymax = swp;
226 }
227
228 DPRINTF("%s mymin:%d mymax:%d lastport:%d\n", __func__,
229 *mymin, *mymax, *lastport);
230
231 *pnext_ephemeral = &portalgo_next_ephemeral[portalgo_proto]
232 [portalgo_af][portalgo_range][algo];
233
234 DPRINTF("%s portalgo_proto:%d portalgo_af:%d portalgo_range:%d\n",
235 __func__, portalgo_proto, portalgo_af, portalgo_range);
236 return 0;
237}
238
239/*
240 * Check whether the port picked by the port randomizer is available
241 * and whether KAUTH approves of our choice. This part of the code
242 * shamelessly copied from in_pcb.c.
243 */
244static bool
245check_suitable_port(uint16_t port, struct inpcb_hdr *inp_hdr, kauth_cred_t cred)
246{
247 struct inpcbtable * const table = inp_hdr->inph_table;
248#ifdef INET
249 vestigial_inpcb_t vestigial;
250#endif
251 int error;
252#ifdef INET6
253 struct socket *so;
254 int wild = 0;
255#endif
256
257 DPRINTF("%s called for argument %d\n", __func__, port);
258
259 switch (inp_hdr->inph_af) {
260#ifdef INET
261 case AF_INET: { /* IPv4 */
262 struct inpcb *inp = (struct inpcb *)(void *)inp_hdr;
263 struct inpcb *pcb;
264 struct sockaddr_in sin;
265
266 if (__BITMAP_ISSET(port, &inet4_reserve))
267 return false;
268
269 sin.sin_addr = inp->inp_laddr;
270 pcb = in_pcblookup_port(table, sin.sin_addr, htons(port), 1,
271 &vestigial);
272
273 DPRINTF("%s in_pcblookup_port returned %p and "
274 "vestigial.valid %d\n",
275 __func__, pcb, vestigial.valid);
276
277 if ((!pcb) && (!vestigial.valid)) {
278 enum kauth_network_req req;
279
280 /* We have a free port. Check with the secmodel. */
281 if (inp->inp_flags & INP_LOWPORT) {
282#ifndef IPNOPRIVPORTS
283 req = KAUTH_REQ_NETWORK_BIND_PRIVPORT;
284#else
285 req = KAUTH_REQ_NETWORK_BIND_PORT;
286#endif
287 } else
288 req = KAUTH_REQ_NETWORK_BIND_PORT;
289
290 sin.sin_port = port;
291 error = kauth_authorize_network(cred,
292 KAUTH_NETWORK_BIND,
293 req, inp->inp_socket, &sin, NULL);
294 DPRINTF("%s kauth_authorize_network returned %d\n",
295 __func__, error);
296
297 if (error == 0) {
298 DPRINTF("%s port approved\n", __func__);
299 return true; /* KAUTH agrees */
300 }
301 }
302 break;
303 }
304#endif
305#ifdef INET6
306 case AF_INET6: { /* IPv6 */
307 struct in6pcb *in6p = (struct in6pcb *)(void *)inp_hdr;
308 struct sockaddr_in6 sin6;
309 void *t;
310
311 if (__BITMAP_ISSET(port, &inet6_reserve))
312 return false;
313
314 sin6.sin6_addr = in6p->in6p_laddr;
315 so = in6p->in6p_socket;
316
317 /* XXX: this is redundant when called from in6_pcbbind */
318 if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
319 ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
320 (so->so_options & SO_ACCEPTCONN) == 0))
321 wild = 1;
322
323#ifdef INET
324 if (IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) {
325 t = in_pcblookup_port(table,
326 *(struct in_addr *)&sin6.sin6_addr.s6_addr32[3],
327 htons(port), wild, &vestigial);
328 if (!t && vestigial.valid) {
329 DPRINTF("%s in_pcblookup_port returned "
330 "a result\n", __func__);
331 return false;
332 }
333 } else
334#endif
335 {
336 t = in6_pcblookup_port(table, &sin6.sin6_addr,
337 htons(port), wild, &vestigial);
338 if (!t && vestigial.valid) {
339 DPRINTF("%s in6_pcblookup_port returned "
340 "a result\n", __func__);
341 return false;
342 }
343 }
344 if (t == NULL) {
345 enum kauth_network_req req;
346
347 /* We have a free port. Check with the secmodel. */
348 if (in6p->in6p_flags & IN6P_LOWPORT) {
349#ifndef IPNOPRIVPORTS
350 req = KAUTH_REQ_NETWORK_BIND_PRIVPORT;
351#else
352 req = KAUTH_REQ_NETWORK_BIND_PORT;
353#endif
354 } else {
355 req = KAUTH_REQ_NETWORK_BIND_PORT;
356 }
357
358 sin6.sin6_port = port;
359 error = kauth_authorize_network(cred,
360 KAUTH_NETWORK_BIND, req, so, &sin6, NULL);
361 if (error) {
362 /* Secmodel says no. Keep looking. */
363 DPRINTF("%s secmodel says no\n", __func__);
364 return false;
365 }
366 DPRINTF("%s port approved\n", __func__);
367 return true;
368 }
369 break;
370 }
371#endif
372 default:
373 DPRINTF("%s unknown address family\n", __func__);
374 return false;
375 }
376 return false;
377}
378
379/* This is the default BSD algorithm, as described in RFC 6056 */
380static int
381algo_bsd(int algo, uint16_t *port, struct inpcb_hdr *inp_hdr, kauth_cred_t cred)
382{
383 uint16_t count;
384 uint16_t mymin, mymax, lastport;
385 uint16_t *next_ephemeral;
386 int error;
387
388 DPRINTF("%s called\n", __func__);
389 error = pcb_getports(inp_hdr, &lastport, &mymin, &mymax,
390 &next_ephemeral, algo);
391 if (error)
392 return error;
393 count = mymax - mymin + 1;
394 do {
395 uint16_t myport = *next_ephemeral;
396
397 if (myport < mymin || mymax < myport)
398 myport = mymax;
399 *next_ephemeral = myport - 1;
400 if (check_suitable_port(myport, inp_hdr, cred)) {
401 *port = myport;
402 DPRINTF("%s returning port %d\n", __func__, *port);
403 return 0;
404 }
405 count--;
406 } while (count > 0);
407
408 DPRINTF("%s returning EAGAIN\n", __func__);
409 return EAGAIN;
410}
411
412/*
413 * The straightforward algorithm that increments the port number
414 * by a random amount.
415 */
416static int
417algo_random_start(int algo, uint16_t *port, struct inpcb_hdr *inp_hdr,
418 kauth_cred_t cred)
419{
420 uint16_t count, num_ephemeral;
421 uint16_t mymin, mymax, lastport;
422 uint16_t *next_ephemeral;
423 int error;
424
425 DPRINTF("%s called\n", __func__);
426
427 error = pcb_getports(inp_hdr, &lastport, &mymin, &mymax,
428 &next_ephemeral, algo);
429 if (error)
430 return error;
431
432 num_ephemeral = mymax - mymin + 1;
433
434 DPRINTF("num_ephemeral: %u\n", num_ephemeral);
435
436 *next_ephemeral = mymin + (cprng_fast32() % num_ephemeral);
437
438 DPRINTF("next_ephemeral initially: %u\n", *next_ephemeral);
439
440 count = num_ephemeral;
441
442 do {
443 if (check_suitable_port(*next_ephemeral, inp_hdr, cred)) {
444 *port = *next_ephemeral;
445 DPRINTF("%s returning port %d\n", __func__, *port);
446 return 0;
447 }
448 if (*next_ephemeral == mymax) {
449 *next_ephemeral = mymin;
450 } else
451 (*next_ephemeral)++;
452
453 count--;
454
455
456 DPRINTF("next_ephemeral: %u count: %u\n", *next_ephemeral,
457 count);
458
459 } while (count > 0);
460
461 DPRINTF("%s returning EINVAL\n", __func__);
462
463 return EINVAL;
464}
465
466/*
467 * Since there is no state kept on the ports tried, we might actually
468 * give up before exhausting the free ports.
469 */
470static int
471algo_random_pick(int algo, uint16_t *port, struct inpcb_hdr *inp_hdr,
472 kauth_cred_t cred)
473{
474 uint16_t count, num_ephemeral;
475 uint16_t mymin, mymax, lastport;
476 uint16_t *next_ephemeral;
477 int error;
478
479 DPRINTF("%s called\n", __func__);
480
481 error = pcb_getports(inp_hdr, &lastport, &mymin, &mymax,
482 &next_ephemeral, algo);
483 if (error)
484 return error;
485
486 num_ephemeral = mymax - mymin + 1;
487
488 DPRINTF("num_ephemeral: %u\n", num_ephemeral);
489 *next_ephemeral = mymin + (cprng_fast32() % num_ephemeral);
490
491 DPRINTF("next_ephemeral initially: %u\n", *next_ephemeral);
492
493 count = num_ephemeral;
494
495 do {
496 if (check_suitable_port(*next_ephemeral, inp_hdr, cred)) {
497 *port = *next_ephemeral;
498 DPRINTF("%s returning port %d\n", __func__, *port);
499 return 0;
500 }
501 *next_ephemeral = mymin +
502 (cprng_fast32() % num_ephemeral);
503
504 count--;
505
506 DPRINTF("next_ephemeral: %u count: %u\n",
507 *next_ephemeral, count);
508 } while (count > 0);
509
510 DPRINTF("%s returning EINVAL\n", __func__);
511
512 return EINVAL;
513}
514
515/* This is the implementation from FreeBSD, with tweaks */
516static uint16_t
517Fhash(const struct inpcb_hdr *inp_hdr)
518{
519 MD5_CTX f_ctx;
520 uint32_t Ff[4];
521 uint32_t secret_f[4];
522 uint32_t offset;
523 uint16_t soffset[2];
524
525 cprng_fast(secret_f, sizeof(secret_f));
526
527 MD5Init(&f_ctx);
528 switch (inp_hdr->inph_af) {
529#ifdef INET
530 case AF_INET: {
531 const struct inpcb *inp =
532 (const struct inpcb *)(const void *)inp_hdr;
533 MD5Update(&f_ctx, (const u_char *)&inp->inp_laddr,
534 sizeof(inp->inp_laddr));
535 MD5Update(&f_ctx, (const u_char *)&inp->inp_faddr,
536 sizeof(inp->inp_faddr));
537 MD5Update(&f_ctx, (const u_char *)&inp->inp_fport,
538 sizeof(inp->inp_fport));
539 break;
540 }
541#endif
542#ifdef INET6
543 case AF_INET6: {
544 const struct in6pcb *in6p =
545 (const struct in6pcb *)(const void *)inp_hdr;
546 MD5Update(&f_ctx, (const u_char *)&in6p->in6p_laddr,
547 sizeof(in6p->in6p_laddr));
548 MD5Update(&f_ctx, (const u_char *)&in6p->in6p_faddr,
549 sizeof(in6p->in6p_faddr));
550 MD5Update(&f_ctx, (const u_char *)&in6p->in6p_fport,
551 sizeof(in6p->in6p_fport));
552 break;
553 }
554#endif
555 default:
556 break;
557 }
558 MD5Update(&f_ctx, (const u_char *)secret_f, sizeof(secret_f));
559 MD5Final((u_char *)&Ff, &f_ctx);
560
561 offset = (Ff[0] ^ Ff[1]) ^ (Ff[2] ^ Ff[3]);
562
563 memcpy(&soffset, &offset, sizeof(soffset));
564
565 return soffset[0] ^ soffset[1];
566}
567
568/*
569 * Checks whether the tuple is complete. If not, marks the pcb for
570 * late binding.
571 */
572static bool
573iscompletetuple(struct inpcb_hdr *inp_hdr)
574{
575#ifdef INET6
576 struct in6pcb *in6p;
577#endif
578
579 switch (inp_hdr->inph_af) {
580#ifdef INET
581 case AF_INET: {
582 struct inpcb *inp = (struct inpcb *)(void *)inp_hdr;
583 if (inp->inp_fport == 0 || in_nullhost(inp->inp_faddr)) {
584 DPRINTF("%s fport or faddr missing, delaying port "
585 "to connect/send\n", __func__);
586 inp->inp_bindportonsend = true;
587 return false;
588 } else {
589 inp->inp_bindportonsend = false;
590 }
591 break;
592 }
593#endif
594#ifdef INET6
595 case AF_INET6: {
596 in6p = (struct in6pcb *)(void *)inp_hdr;
597 if (in6p->in6p_fport == 0 || memcmp(&in6p->in6p_faddr,
598 &in6addr_any, sizeof(in6p->in6p_faddr)) == 0) {
599 DPRINTF("%s fport or faddr missing, delaying port "
600 "to connect/send\n", __func__);
601 in6p->in6p_bindportonsend = true;
602 return false;
603 } else {
604 in6p->in6p_bindportonsend = false;
605 }
606 break;
607 }
608#endif
609 default:
610 DPRINTF("%s incorrect address family\n", __func__);
611 return false;
612 }
613
614 return true;
615}
616
617static int
618algo_hash(int algo, uint16_t *port, struct inpcb_hdr *inp_hdr,
619 kauth_cred_t cred)
620{
621 uint16_t count, num_ephemeral;
622 uint16_t mymin, mymax, lastport;
623 uint16_t *next_ephemeral;
624 uint16_t offset, myport;
625 int error;
626
627 DPRINTF("%s called\n", __func__);
628
629 error = pcb_getports(inp_hdr, &lastport, &mymin, &mymax,
630 &next_ephemeral, algo);
631 if (error)
632 return error;
633
634 if (!iscompletetuple(inp_hdr)) {
635 *port = 0;
636 return 0;
637 }
638
639 /* Ephemeral port selection function */
640 num_ephemeral = mymax - mymin + 1;
641
642 DPRINTF("num_ephemeral: %d\n", num_ephemeral);
643
644 offset = Fhash(inp_hdr);
645
646 count = num_ephemeral;
647 do {
648 myport = mymin + (*next_ephemeral + offset)
649 % num_ephemeral;
650
651 (*next_ephemeral)++;
652
653 if (check_suitable_port(myport, inp_hdr, cred)) {
654 *port = myport;
655 DPRINTF("%s returning port %d\n", __func__, *port);
656 return 0;
657 }
658 count--;
659 } while (count > 0);
660
661 DPRINTF("%s returning EINVAL\n", __func__);
662
663 return EINVAL;
664}
665
666static int
667algo_doublehash(int algo, uint16_t *port, struct inpcb_hdr *inp_hdr,
668 kauth_cred_t cred)
669{
670 uint16_t count, num_ephemeral;
671 uint16_t mymin, mymax, lastport;
672 uint16_t *next_ephemeral;
673 uint16_t offset, myport;
674 static uint16_t dhtable[8];
675 size_t idx;
676 int error;
677
678 DPRINTF("%s called\n", __func__);
679
680 error = pcb_getports(inp_hdr, &lastport, &mymin, &mymax,
681 &next_ephemeral, algo);
682 if (error)
683 return error;
684
685 if (!iscompletetuple(inp_hdr)) {
686 *port = 0;
687 return 0;
688 }
689 /* first time initialization */
690 if (dhtable[0] == 0)
691 for (size_t i = 0; i < __arraycount(dhtable); i++)
692 dhtable[i] = cprng_fast32() & 0xffff;
693
694 /* Ephemeral port selection function */
695 num_ephemeral = mymax - mymin + 1;
696 offset = Fhash(inp_hdr);
697 idx = Fhash(inp_hdr) % __arraycount(dhtable); /* G */
698 count = num_ephemeral;
699
700 do {
701 myport = mymin + (offset + dhtable[idx])
702 % num_ephemeral;
703 dhtable[idx]++;
704
705 if (check_suitable_port(myport, inp_hdr, cred)) {
706 *port = myport;
707 DPRINTF("%s returning port %d\n", __func__, *port);
708 return 0;
709 }
710 count--;
711
712 } while (count > 0);
713
714 DPRINTF("%s returning EINVAL\n", __func__);
715
716 return EINVAL;
717}
718
719static int
720algo_randinc(int algo, uint16_t *port, struct inpcb_hdr *inp_hdr,
721 kauth_cred_t cred)
722{
723 static const uint16_t N = 500; /* Determines the trade-off */
724 uint16_t count, num_ephemeral;
725 uint16_t mymin, mymax, lastport;
726 uint16_t *next_ephemeral;
727 uint16_t myport;
728 int error;
729
730 DPRINTF("%s called\n", __func__);
731
732 error = pcb_getports(inp_hdr, &lastport, &mymin, &mymax,
733 &next_ephemeral, algo);
734 if (error)
735 return error;
736
737 if (*next_ephemeral == 0)
738 *next_ephemeral = cprng_fast32() & 0xffff;
739
740 /* Ephemeral port selection function */
741 num_ephemeral = mymax - mymin + 1;
742
743 count = num_ephemeral;
744 do {
745 *next_ephemeral = *next_ephemeral +
746 (cprng_fast32() % N) + 1;
747 myport = mymin +
748 (*next_ephemeral % num_ephemeral);
749
750 if (check_suitable_port(myport, inp_hdr, cred)) {
751 *port = myport;
752 DPRINTF("%s returning port %d\n", __func__, *port);
753 return 0;
754 }
755 count--;
756 } while (count > 0);
757
758 return EINVAL;
759}
760
761/* The generic function called in order to pick a port. */
762int
763portalgo_randport(uint16_t *port, struct inpcb_hdr *inp_hdr, kauth_cred_t cred)
764{
765 int algo, error;
766 uint16_t lport;
767 int default_algo;
768
769 DPRINTF("%s called\n", __func__);
770
771 if (inp_hdr->inph_portalgo == PORTALGO_DEFAULT) {
772 switch (inp_hdr->inph_af) {
773#ifdef INET
774 case AF_INET:
775 default_algo = inet4_portalgo;
776 break;
777#endif
778#ifdef INET6
779 case AF_INET6:
780 default_algo = inet6_portalgo;
781 break;
782#endif
783 default:
784 return EINVAL;
785 }
786
787 if (default_algo == PORTALGO_DEFAULT)
788 algo = PORTALGO_BSD;
789 else
790 algo = default_algo;
791 }
792 else /* socket specifies the algorithm */
793 algo = inp_hdr->inph_portalgo;
794
795 KASSERT(algo >= 0);
796 KASSERT(algo < NALGOS);
797
798 switch (inp_hdr->inph_af) {
799#ifdef INET
800 case AF_INET: {
801 char buf[INET_ADDRSTRLEN];
802 struct inpcb *inp = (struct inpcb *)(void *)inp_hdr;
803 DPRINTF("local addr: %s\n", IN_PRINT(buf, &inp->inp_laddr));
804 DPRINTF("local port: %d\n", inp->inp_lport);
805 DPRINTF("foreign addr: %s\n", IN_PRINT(buf, &inp->inp_faddr));
806 DPRINTF("foreign port: %d\n", inp->inp_fport);
807 break;
808 }
809#endif
810#ifdef INET6
811 case AF_INET6: {
812 char buf[INET6_ADDRSTRLEN];
813 struct in6pcb *in6p = (struct in6pcb *)(void *)inp_hdr;
814
815 DPRINTF("local addr: %s\n", IN6_PRINT(buf, &in6p->in6p_laddr));
816 DPRINTF("local port: %d\n", in6p->in6p_lport);
817 DPRINTF("foreign addr: %s\n", IN6_PRINT(buf,
818 &in6p->in6p_laddr));
819 DPRINTF("foreign port: %d\n", in6p->in6p_fport);
820 break;
821 }
822#endif
823 default:
824 break;
825 }
826
827 DPRINTF("%s portalgo = %d\n", __func__, algo);
828
829 error = (*algos[algo].func)(algo, &lport, inp_hdr, cred);
830 if (error == 0) {
831 *port = lport;
832 } else if (error != EAGAIN) {
833 uint16_t lastport, mymin, mymax, *pnext_ephemeral;
834
835 error = pcb_getports(inp_hdr, &lastport, &mymin,
836 &mymax, &pnext_ephemeral, algo);
837 if (error)
838 return error;
839 *port = lastport - 1;
840 }
841 return error;
842}
843
844/* Sets the algorithm to be used globally */
845static int
846portalgo_algo_name_select(const char *name, int *algo)
847{
848 size_t ai;
849
850 DPRINTF("%s called\n", __func__);
851
852 for (ai = 0; ai < NALGOS; ai++)
853 if (strcmp(algos[ai].name, name) == 0) {
854 DPRINTF("%s: found idx %zu\n", __func__, ai);
855 *algo = ai;
856 return 0;
857 }
858 return EINVAL;
859}
860
861/* Sets the algorithm to be used by the pcb inp. */
862int
863portalgo_algo_index_select(struct inpcb_hdr *inp, int algo)
864{
865
866 DPRINTF("%s called with algo %d for pcb %p\n", __func__, algo, inp );
867
868 if ((algo < 0 || algo >= NALGOS) &&
869 (algo != PORTALGO_DEFAULT))
870 return EINVAL;
871
872 inp->inph_portalgo = algo;
873 return 0;
874}
875
876/*
877 * The sysctl hook that is supposed to check that we are picking one
878 * of the valid algorithms.
879 */
880static int
881sysctl_portalgo_selected(SYSCTLFN_ARGS, int *algo)
882{
883 struct sysctlnode node;
884 int error;
885 char newalgo[PORTALGO_MAXLEN];
886
887 DPRINTF("%s called\n", __func__);
888
889 strlcpy(newalgo, algos[*algo].name, sizeof(newalgo));
890
891 node = *rnode;
892 node.sysctl_data = newalgo;
893 node.sysctl_size = sizeof(newalgo);
894
895 error = sysctl_lookup(SYSCTLFN_CALL(&node));
896
897 DPRINTF("newalgo: %s\n", newalgo);
898
899 if (error || newp == NULL ||
900 strncmp(newalgo, algos[*algo].name, sizeof(newalgo)) == 0)
901 return error;
902
903#ifdef KAUTH_NETWORK_SOCKET_PORT_RANDOMIZE
904 if (l != NULL && (error = kauth_authorize_system(l->l_cred,
905 KAUTH_NETWORK_SOCKET, KAUTH_NETWORK_SOCKET_PORT_RANDOMIZE, newname,
906 NULL, NULL)) != 0)
907 return error;
908#endif
909
910 mutex_enter(softnet_lock);
911 error = portalgo_algo_name_select(newalgo, algo);
912 mutex_exit(softnet_lock);
913 return error;
914}
915
916static int
917sysctl_portalgo_reserve(SYSCTLFN_ARGS, bitmap *bt)
918{
919 struct sysctlnode node;
920 int error;
921
922 DPRINTF("%s called\n", __func__);
923
924 node = *rnode;
925 node.sysctl_data = bt;
926 node.sysctl_size = sizeof(*bt);
927
928 error = sysctl_lookup(SYSCTLFN_CALL(&node));
929
930 if (error || newp == NULL)
931 return error;
932
933#ifdef KAUTH_NETWORK_SOCKET_PORT_RESERVE
934 if (l != NULL && (error = kauth_authorize_system(l->l_cred,
935 KAUTH_NETWORK_SOCKET, KAUTH_NETWORK_SOCKET_PORT_RESERVE, bt,
936 NULL, NULL)) != 0)
937 return error;
938#endif
939 return error;
940}
941
942#ifdef INET
943/*
944 * The sysctl hook that is supposed to check that we are picking one
945 * of the valid algorithms.
946 */
947int
948sysctl_portalgo_selected4(SYSCTLFN_ARGS)
949{
950
951 return sysctl_portalgo_selected(SYSCTLFN_CALL(rnode), &inet4_portalgo);
952}
953
954int
955sysctl_portalgo_reserve4(SYSCTLFN_ARGS)
956{
957
958 return sysctl_portalgo_reserve(SYSCTLFN_CALL(rnode), &inet4_reserve);
959}
960#endif
961
962#ifdef INET6
963int
964sysctl_portalgo_selected6(SYSCTLFN_ARGS)
965{
966
967 return sysctl_portalgo_selected(SYSCTLFN_CALL(rnode), &inet6_portalgo);
968}
969
970int
971sysctl_portalgo_reserve6(SYSCTLFN_ARGS)
972{
973 return sysctl_portalgo_reserve(SYSCTLFN_CALL(rnode), &inet6_reserve);
974}
975#endif
976
977/*
978 * The sysctl hook that returns the available
979 * algorithms.
980 */
981int
982sysctl_portalgo_available(SYSCTLFN_ARGS)
983{
984 size_t ai, len = 0;
985 struct sysctlnode node;
986 char availalgo[NALGOS * PORTALGO_MAXLEN];
987
988 DPRINTF("%s called\n", __func__);
989
990 availalgo[0] = '\0';
991
992 for (ai = 0; ai < NALGOS; ai++) {
993 len = strlcat(availalgo, algos[ai].name, sizeof(availalgo));
994 if (ai < NALGOS - 1)
995 strlcat(availalgo, " ", sizeof(availalgo));
996 }
997
998 DPRINTF("available algos: %s\n", availalgo);
999
1000 node = *rnode;
1001 node.sysctl_data = availalgo;
1002 node.sysctl_size = len;
1003
1004 return sysctl_lookup(SYSCTLFN_CALL(&node));
1005}
1006