Home | History | Annotate | Line # | Download | only in netinet
portalgo.c revision 1.9
      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