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