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