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