1*b000e63fSozaki-r /* $NetBSD: portalgo.c,v 1.15 2022/11/04 09:01:53 ozaki-r Exp $ */
284f52095Schristos
384f52095Schristos /*
484f52095Schristos * Copyright 2011 Vlad Balan
584f52095Schristos *
684f52095Schristos * Written by Vlad Balan for the NetBSD Foundation.
784f52095Schristos *
884f52095Schristos * Redistribution and use in source and binary forms, with or without
984f52095Schristos * modification, are permitted provided that the following conditions
1084f52095Schristos * are met:
1184f52095Schristos * 1. Redistributions of source code must retain the above copyright
1284f52095Schristos * notice, this list of conditions and the following disclaimer.
1384f52095Schristos * 2. Redistributions in binary form must reproduce the above copyright
1484f52095Schristos * notice, this list of conditions and the following disclaimer in the
1584f52095Schristos * documentation and/or other materials provided with the distribution.
1684f52095Schristos *
1784f52095Schristos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1884f52095Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1984f52095Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2084f52095Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2184f52095Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2284f52095Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2384f52095Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2484f52095Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2584f52095Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2684f52095Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2784f52095Schristos * SUCH DAMAGE.
2884f52095Schristos *
2984f52095Schristos */
3084f52095Schristos
3184f52095Schristos /*
3284f52095Schristos * see:
3384f52095Schristos * RFC 6056 Recommendations for Transport-Protocol Port Randomization
3484f52095Schristos */
3584f52095Schristos
3684f52095Schristos #include <sys/cdefs.h>
37*b000e63fSozaki-r __KERNEL_RCSID(0, "$NetBSD: portalgo.c,v 1.15 2022/11/04 09:01:53 ozaki-r Exp $");
3884f52095Schristos
391c4a50f1Spooka #ifdef _KERNEL_OPT
4084f52095Schristos #include "opt_inet.h"
411c4a50f1Spooka #endif
4284f52095Schristos
4384f52095Schristos #include <sys/param.h>
4484f52095Schristos #include <sys/errno.h>
4584f52095Schristos #include <sys/kauth.h>
4684f52095Schristos #include <sys/uidinfo.h>
4784f52095Schristos #include <sys/md5.h>
4884f52095Schristos #include <sys/cprng.h>
49586fa638Schristos #include <sys/bitops.h>
5084f52095Schristos
5184f52095Schristos #include <net/if.h>
5284f52095Schristos
5384f52095Schristos #include <netinet/in.h>
5484f52095Schristos #include <netinet/in_systm.h>
5584f52095Schristos #include <netinet/ip.h>
5684f52095Schristos #include <netinet/in_pcb.h>
5784f52095Schristos #include <netinet/in_var.h>
5884f52095Schristos #include <netinet/ip_var.h>
5984f52095Schristos
6084f52095Schristos #ifdef INET6
6184f52095Schristos #include <netinet/ip6.h>
6284f52095Schristos #include <netinet6/ip6_var.h>
6384f52095Schristos #include <netinet6/in6_pcb.h>
6484f52095Schristos #endif
6584f52095Schristos
6684f52095Schristos #include <netinet/tcp_vtw.h>
6784f52095Schristos
6884f52095Schristos #include "portalgo.h"
6984f52095Schristos
7084f52095Schristos #define NPROTO 2
7184f52095Schristos #define PORTALGO_TCP 0
7284f52095Schristos #define PORTALGO_UDP 1
7384f52095Schristos
7484f52095Schristos #define NAF 2
7584f52095Schristos #define PORTALGO_IPV4 0
7684f52095Schristos #define PORTALGO_IPV6 1
7784f52095Schristos
7884f52095Schristos #define NRANGES 2
7984f52095Schristos #define PORTALGO_LOWPORT 0
8084f52095Schristos #define PORTALGO_HIGHPORT 1
8184f52095Schristos
8284f52095Schristos #if PORTALGO_DEBUG
8384f52095Schristos static bool portalgo_debug = true;
8484f52095Schristos #define DPRINTF if (portalgo_debug) printf
8584f52095Schristos #else
8684f52095Schristos #define DPRINTF while (/*CONSTCOND*/0) printf
8784f52095Schristos #endif
8884f52095Schristos
8983023c7fSpooka #ifndef PORTALGO_INET4_DEFAULT
9083023c7fSpooka #define PORTALGO_INET4_DEFAULT PORTALGO_BSD
9183023c7fSpooka #endif
9283023c7fSpooka #ifndef PORTALGO_INET6_DEFAULT
9383023c7fSpooka #define PORTALGO_INET6_DEFAULT PORTALGO_BSD
9483023c7fSpooka #endif
9583023c7fSpooka
969878d5e5Schristos typedef __BITMAP_TYPE(, uint32_t, 0x10000) bitmap;
9784f52095Schristos #ifdef INET
9883023c7fSpooka static int inet4_portalgo = PORTALGO_INET4_DEFAULT;
999878d5e5Schristos static bitmap inet4_reserve;
10084f52095Schristos #endif
10184f52095Schristos #ifdef INET6
10283023c7fSpooka static int inet6_portalgo = PORTALGO_INET6_DEFAULT;
1039878d5e5Schristos static bitmap inet6_reserve;
10484f52095Schristos #endif
10584f52095Schristos
10684f52095Schristos typedef struct {
10784f52095Schristos const char *name;
1080e390eeeSozaki-r int (*func)(int, uint16_t *, struct inpcb *, kauth_cred_t);
10984f52095Schristos } portalgo_algorithm_t;
11084f52095Schristos
1110e390eeeSozaki-r static int algo_bsd(int, uint16_t *, struct inpcb *, kauth_cred_t);
1120e390eeeSozaki-r static int algo_random_start(int, uint16_t *, struct inpcb *, kauth_cred_t);
1130e390eeeSozaki-r static int algo_random_pick(int, uint16_t *, struct inpcb *, kauth_cred_t);
1140e390eeeSozaki-r static int algo_hash(int, uint16_t *, struct inpcb *, kauth_cred_t);
1150e390eeeSozaki-r static int algo_doublehash(int, uint16_t *, struct inpcb *, kauth_cred_t);
1160e390eeeSozaki-r static int algo_randinc(int, uint16_t *, struct inpcb *, kauth_cred_t);
11784f52095Schristos
11884f52095Schristos static const portalgo_algorithm_t algos[] = {
11984f52095Schristos {
12084f52095Schristos .name = "bsd",
12184f52095Schristos .func = algo_bsd
12284f52095Schristos },
12384f52095Schristos {
12484f52095Schristos .name = "random_start",
12584f52095Schristos .func = algo_random_start
12684f52095Schristos },
12784f52095Schristos {
12884f52095Schristos .name = "random_pick",
12984f52095Schristos .func = algo_random_pick
13084f52095Schristos },
13184f52095Schristos {
13284f52095Schristos .name = "hash",
13384f52095Schristos .func = algo_hash
13484f52095Schristos },
13584f52095Schristos {
13684f52095Schristos .name = "doublehash",
13784f52095Schristos .func = algo_doublehash
13884f52095Schristos },
13984f52095Schristos {
14084f52095Schristos .name = "randinc",
14184f52095Schristos .func = algo_randinc
14284f52095Schristos }
14384f52095Schristos };
14484f52095Schristos
14584f52095Schristos #define NALGOS __arraycount(algos)
14684f52095Schristos
14784f52095Schristos static uint16_t portalgo_next_ephemeral[NPROTO][NAF][NRANGES][NALGOS];
14884f52095Schristos
14984f52095Schristos /*
15084f52095Schristos * Access the pcb and copy the values of the last port and the ends of
15184f52095Schristos * the port range.
15284f52095Schristos */
15384f52095Schristos static int
pcb_getports(struct inpcb * inp,uint16_t * lastport,uint16_t * mymin,uint16_t * mymax,uint16_t ** pnext_ephemeral,int algo)1540e390eeeSozaki-r pcb_getports(struct inpcb *inp, uint16_t *lastport,
15584f52095Schristos uint16_t *mymin, uint16_t *mymax, uint16_t **pnext_ephemeral, int algo)
15684f52095Schristos {
1570e390eeeSozaki-r struct inpcbtable * const table = inp->inp_table;
15884f52095Schristos struct socket *so;
15984f52095Schristos int portalgo_proto;
16084f52095Schristos int portalgo_af;
16184f52095Schristos int portalgo_range;
16284f52095Schristos
1630e390eeeSozaki-r so = inp->inp_socket;
16484f52095Schristos switch (so->so_type) {
16584f52095Schristos case SOCK_DGRAM: /* UDP or DCCP */
16665278823Srjs case SOCK_CONN_DGRAM:
16784f52095Schristos portalgo_proto = PORTALGO_UDP;
16884f52095Schristos break;
16984f52095Schristos case SOCK_STREAM: /* TCP or SCTP */
17084f52095Schristos portalgo_proto = PORTALGO_TCP;
17184f52095Schristos break;
17284f52095Schristos default:
17384f52095Schristos return EPFNOSUPPORT;
17484f52095Schristos }
17584f52095Schristos
1760e390eeeSozaki-r switch (inp->inp_af) {
17784f52095Schristos #ifdef INET
17884f52095Schristos case AF_INET: {
17984f52095Schristos portalgo_af = PORTALGO_IPV4;
18084f52095Schristos if (inp->inp_flags & INP_LOWPORT) {
18184f52095Schristos *mymin = lowportmin;
18284f52095Schristos *mymax = lowportmax;
18384f52095Schristos *lastport = table->inpt_lastlow;
18484f52095Schristos portalgo_range = PORTALGO_LOWPORT;
18584f52095Schristos } else {
18684f52095Schristos *mymin = anonportmin;
18784f52095Schristos *mymax = anonportmax;
18884f52095Schristos *lastport = table->inpt_lastport;
18984f52095Schristos portalgo_range = PORTALGO_HIGHPORT;
19084f52095Schristos }
19184f52095Schristos break;
19284f52095Schristos }
19384f52095Schristos #endif
19484f52095Schristos #ifdef INET6
19584f52095Schristos case AF_INET6: {
19684f52095Schristos portalgo_af = PORTALGO_IPV6;
1970e390eeeSozaki-r if (inp->inp_flags & IN6P_LOWPORT) {
19884f52095Schristos *mymin = ip6_lowportmin;
19984f52095Schristos *mymax = ip6_lowportmax;
20084f52095Schristos *lastport = table->inpt_lastlow;
20184f52095Schristos portalgo_range = PORTALGO_LOWPORT;
20284f52095Schristos } else {
20384f52095Schristos *mymin = ip6_anonportmin;
20484f52095Schristos *mymax = ip6_anonportmax;
20584f52095Schristos *lastport = table->inpt_lastport;
20684f52095Schristos portalgo_range = PORTALGO_HIGHPORT;
20784f52095Schristos }
20884f52095Schristos break;
20984f52095Schristos }
21084f52095Schristos #endif
21184f52095Schristos default:
21284f52095Schristos return EAFNOSUPPORT;
21384f52095Schristos }
21484f52095Schristos
21584f52095Schristos if (*mymin > *mymax) { /* sanity check */
21684f52095Schristos u_int16_t swp;
21784f52095Schristos
21884f52095Schristos swp = *mymin;
21984f52095Schristos *mymin = *mymax;
22084f52095Schristos *mymax = swp;
22184f52095Schristos }
22284f52095Schristos
22384f52095Schristos DPRINTF("%s mymin:%d mymax:%d lastport:%d\n", __func__,
22484f52095Schristos *mymin, *mymax, *lastport);
22584f52095Schristos
22684f52095Schristos *pnext_ephemeral = &portalgo_next_ephemeral[portalgo_proto]
22784f52095Schristos [portalgo_af][portalgo_range][algo];
22884f52095Schristos
22984f52095Schristos DPRINTF("%s portalgo_proto:%d portalgo_af:%d portalgo_range:%d\n",
23084f52095Schristos __func__, portalgo_proto, portalgo_af, portalgo_range);
23184f52095Schristos return 0;
23284f52095Schristos }
23384f52095Schristos
23484f52095Schristos /*
23584f52095Schristos * Check whether the port picked by the port randomizer is available
23684f52095Schristos * and whether KAUTH approves of our choice. This part of the code
23784f52095Schristos * shamelessly copied from in_pcb.c.
23884f52095Schristos */
23984f52095Schristos static bool
check_suitable_port(uint16_t port,struct inpcb * inp,kauth_cred_t cred)2400e390eeeSozaki-r check_suitable_port(uint16_t port, struct inpcb *inp, kauth_cred_t cred)
24184f52095Schristos {
2420e390eeeSozaki-r struct inpcbtable * const table = inp->inp_table;
24384f52095Schristos #ifdef INET
24484f52095Schristos vestigial_inpcb_t vestigial;
24584f52095Schristos #endif
24684f52095Schristos int error;
24784f52095Schristos #ifdef INET6
24884f52095Schristos struct socket *so;
24984f52095Schristos int wild = 0;
25084f52095Schristos #endif
25184f52095Schristos
25284f52095Schristos DPRINTF("%s called for argument %d\n", __func__, port);
25384f52095Schristos
2540e390eeeSozaki-r switch (inp->inp_af) {
25584f52095Schristos #ifdef INET
25684f52095Schristos case AF_INET: { /* IPv4 */
25784f52095Schristos struct inpcb *pcb;
25884f52095Schristos struct sockaddr_in sin;
25984f52095Schristos
2609878d5e5Schristos if (__BITMAP_ISSET(port, &inet4_reserve))
261cbf1f72bSchristos return false;
262cbf1f72bSchristos
263a071c829Sozaki-r sin.sin_addr = in4p_laddr(inp);
2642ba9f052Sozaki-r pcb = inpcb_lookup_local(table, sin.sin_addr, htons(port), 1,
26584f52095Schristos &vestigial);
26684f52095Schristos
2672ba9f052Sozaki-r DPRINTF("%s inpcb_lookup_local returned %p and "
26884f52095Schristos "vestigial.valid %d\n",
26984f52095Schristos __func__, pcb, vestigial.valid);
27084f52095Schristos
27184f52095Schristos if ((!pcb) && (!vestigial.valid)) {
27284f52095Schristos enum kauth_network_req req;
27384f52095Schristos
27484f52095Schristos /* We have a free port. Check with the secmodel. */
27584f52095Schristos if (inp->inp_flags & INP_LOWPORT) {
27684f52095Schristos #ifndef IPNOPRIVPORTS
27784f52095Schristos req = KAUTH_REQ_NETWORK_BIND_PRIVPORT;
27884f52095Schristos #else
27984f52095Schristos req = KAUTH_REQ_NETWORK_BIND_PORT;
28084f52095Schristos #endif
28184f52095Schristos } else
28284f52095Schristos req = KAUTH_REQ_NETWORK_BIND_PORT;
28384f52095Schristos
28484f52095Schristos sin.sin_port = port;
28584f52095Schristos error = kauth_authorize_network(cred,
28684f52095Schristos KAUTH_NETWORK_BIND,
28784f52095Schristos req, inp->inp_socket, &sin, NULL);
28884f52095Schristos DPRINTF("%s kauth_authorize_network returned %d\n",
28984f52095Schristos __func__, error);
29084f52095Schristos
29184f52095Schristos if (error == 0) {
29284f52095Schristos DPRINTF("%s port approved\n", __func__);
29384f52095Schristos return true; /* KAUTH agrees */
29484f52095Schristos }
29584f52095Schristos }
29684f52095Schristos break;
29784f52095Schristos }
29884f52095Schristos #endif
29984f52095Schristos #ifdef INET6
30084f52095Schristos case AF_INET6: { /* IPv6 */
30184f52095Schristos struct sockaddr_in6 sin6;
30284f52095Schristos void *t;
30384f52095Schristos
3049878d5e5Schristos if (__BITMAP_ISSET(port, &inet6_reserve))
305cbf1f72bSchristos return false;
306cbf1f72bSchristos
307a071c829Sozaki-r sin6.sin6_addr = in6p_laddr(inp);
3080e390eeeSozaki-r so = inp->inp_socket;
30984f52095Schristos
310*b000e63fSozaki-r /* XXX: this is redundant when called from in6pcb_bind */
31184f52095Schristos if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
31284f52095Schristos ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
31384f52095Schristos (so->so_options & SO_ACCEPTCONN) == 0))
31484f52095Schristos wild = 1;
31584f52095Schristos
31684f52095Schristos #ifdef INET
31784f52095Schristos if (IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) {
3182ba9f052Sozaki-r t = inpcb_lookup_local(table,
31984f52095Schristos *(struct in_addr *)&sin6.sin6_addr.s6_addr32[3],
32084f52095Schristos htons(port), wild, &vestigial);
32184f52095Schristos if (!t && vestigial.valid) {
3222ba9f052Sozaki-r DPRINTF("%s inpcb_lookup_local returned "
32384f52095Schristos "a result\n", __func__);
32484f52095Schristos return false;
32584f52095Schristos }
32684f52095Schristos } else
32784f52095Schristos #endif
32884f52095Schristos {
329*b000e63fSozaki-r t = in6pcb_lookup_local(table, &sin6.sin6_addr,
33084f52095Schristos htons(port), wild, &vestigial);
33184f52095Schristos if (!t && vestigial.valid) {
332*b000e63fSozaki-r DPRINTF("%s in6pcb_lookup_local returned "
33384f52095Schristos "a result\n", __func__);
33484f52095Schristos return false;
33584f52095Schristos }
33684f52095Schristos }
33784f52095Schristos if (t == NULL) {
33884f52095Schristos enum kauth_network_req req;
33984f52095Schristos
34084f52095Schristos /* We have a free port. Check with the secmodel. */
3410e390eeeSozaki-r if (inp->inp_flags & IN6P_LOWPORT) {
34284f52095Schristos #ifndef IPNOPRIVPORTS
34384f52095Schristos req = KAUTH_REQ_NETWORK_BIND_PRIVPORT;
34484f52095Schristos #else
34584f52095Schristos req = KAUTH_REQ_NETWORK_BIND_PORT;
34684f52095Schristos #endif
34784f52095Schristos } else {
34884f52095Schristos req = KAUTH_REQ_NETWORK_BIND_PORT;
34984f52095Schristos }
35084f52095Schristos
35184f52095Schristos sin6.sin6_port = port;
35284f52095Schristos error = kauth_authorize_network(cred,
35384f52095Schristos KAUTH_NETWORK_BIND, req, so, &sin6, NULL);
35484f52095Schristos if (error) {
35584f52095Schristos /* Secmodel says no. Keep looking. */
35684f52095Schristos DPRINTF("%s secmodel says no\n", __func__);
35784f52095Schristos return false;
35884f52095Schristos }
35984f52095Schristos DPRINTF("%s port approved\n", __func__);
36084f52095Schristos return true;
36184f52095Schristos }
36284f52095Schristos break;
36384f52095Schristos }
36484f52095Schristos #endif
36584f52095Schristos default:
36684f52095Schristos DPRINTF("%s unknown address family\n", __func__);
36784f52095Schristos return false;
36884f52095Schristos }
36984f52095Schristos return false;
37084f52095Schristos }
37184f52095Schristos
37284f52095Schristos /* This is the default BSD algorithm, as described in RFC 6056 */
37384f52095Schristos static int
algo_bsd(int algo,uint16_t * port,struct inpcb * inp,kauth_cred_t cred)3740e390eeeSozaki-r algo_bsd(int algo, uint16_t *port, struct inpcb *inp, kauth_cred_t cred)
37584f52095Schristos {
37684f52095Schristos uint16_t count;
37784f52095Schristos uint16_t mymin, mymax, lastport;
37884f52095Schristos uint16_t *next_ephemeral;
37984f52095Schristos int error;
38084f52095Schristos
38184f52095Schristos DPRINTF("%s called\n", __func__);
3820e390eeeSozaki-r error = pcb_getports(inp, &lastport, &mymin, &mymax,
38384f52095Schristos &next_ephemeral, algo);
38484f52095Schristos if (error)
38584f52095Schristos return error;
38684f52095Schristos count = mymax - mymin + 1;
38784f52095Schristos do {
38884f52095Schristos uint16_t myport = *next_ephemeral;
38984f52095Schristos
39084f52095Schristos if (myport < mymin || mymax < myport)
39184f52095Schristos myport = mymax;
39284f52095Schristos *next_ephemeral = myport - 1;
3930e390eeeSozaki-r if (check_suitable_port(myport, inp, cred)) {
39484f52095Schristos *port = myport;
39584f52095Schristos DPRINTF("%s returning port %d\n", __func__, *port);
39684f52095Schristos return 0;
39784f52095Schristos }
39884f52095Schristos count--;
39984f52095Schristos } while (count > 0);
40084f52095Schristos
40184f52095Schristos DPRINTF("%s returning EAGAIN\n", __func__);
40284f52095Schristos return EAGAIN;
40384f52095Schristos }
40484f52095Schristos
40584f52095Schristos /*
4068403248fSjoerg * The straightforward algorithm that increments the port number
4078403248fSjoerg * by a random amount.
40884f52095Schristos */
40984f52095Schristos static int
algo_random_start(int algo,uint16_t * port,struct inpcb * inp,kauth_cred_t cred)4100e390eeeSozaki-r algo_random_start(int algo, uint16_t *port, struct inpcb *inp,
41184f52095Schristos kauth_cred_t cred)
41284f52095Schristos {
41384f52095Schristos uint16_t count, num_ephemeral;
41484f52095Schristos uint16_t mymin, mymax, lastport;
41584f52095Schristos uint16_t *next_ephemeral;
41684f52095Schristos int error;
41784f52095Schristos
41884f52095Schristos DPRINTF("%s called\n", __func__);
41984f52095Schristos
4200e390eeeSozaki-r error = pcb_getports(inp, &lastport, &mymin, &mymax,
42184f52095Schristos &next_ephemeral, algo);
42284f52095Schristos if (error)
42384f52095Schristos return error;
42484f52095Schristos
42584f52095Schristos num_ephemeral = mymax - mymin + 1;
42684f52095Schristos
42784f52095Schristos DPRINTF("num_ephemeral: %u\n", num_ephemeral);
42884f52095Schristos
42984f52095Schristos *next_ephemeral = mymin + (cprng_fast32() % num_ephemeral);
43084f52095Schristos
43184f52095Schristos DPRINTF("next_ephemeral initially: %u\n", *next_ephemeral);
43284f52095Schristos
43384f52095Schristos count = num_ephemeral;
43484f52095Schristos
43584f52095Schristos do {
4360e390eeeSozaki-r if (check_suitable_port(*next_ephemeral, inp, cred)) {
43784f52095Schristos *port = *next_ephemeral;
43884f52095Schristos DPRINTF("%s returning port %d\n", __func__, *port);
43984f52095Schristos return 0;
44084f52095Schristos }
44184f52095Schristos if (*next_ephemeral == mymax) {
44284f52095Schristos *next_ephemeral = mymin;
44384f52095Schristos } else
44484f52095Schristos (*next_ephemeral)++;
44584f52095Schristos
44684f52095Schristos count--;
44784f52095Schristos
44884f52095Schristos
44984f52095Schristos DPRINTF("next_ephemeral: %u count: %u\n", *next_ephemeral,
45084f52095Schristos count);
45184f52095Schristos
45284f52095Schristos } while (count > 0);
45384f52095Schristos
45484f52095Schristos DPRINTF("%s returning EINVAL\n", __func__);
45584f52095Schristos
45684f52095Schristos return EINVAL;
45784f52095Schristos }
45884f52095Schristos
45984f52095Schristos /*
46084f52095Schristos * Since there is no state kept on the ports tried, we might actually
46184f52095Schristos * give up before exhausting the free ports.
46284f52095Schristos */
46384f52095Schristos static int
algo_random_pick(int algo,uint16_t * port,struct inpcb * inp,kauth_cred_t cred)4640e390eeeSozaki-r algo_random_pick(int algo, uint16_t *port, struct inpcb *inp,
46584f52095Schristos kauth_cred_t cred)
46684f52095Schristos {
46784f52095Schristos uint16_t count, num_ephemeral;
46884f52095Schristos uint16_t mymin, mymax, lastport;
46984f52095Schristos uint16_t *next_ephemeral;
47084f52095Schristos int error;
47184f52095Schristos
47284f52095Schristos DPRINTF("%s called\n", __func__);
47384f52095Schristos
4740e390eeeSozaki-r error = pcb_getports(inp, &lastport, &mymin, &mymax,
47584f52095Schristos &next_ephemeral, algo);
47684f52095Schristos if (error)
47784f52095Schristos return error;
47884f52095Schristos
47984f52095Schristos num_ephemeral = mymax - mymin + 1;
48084f52095Schristos
48184f52095Schristos DPRINTF("num_ephemeral: %u\n", num_ephemeral);
48284f52095Schristos *next_ephemeral = mymin + (cprng_fast32() % num_ephemeral);
48384f52095Schristos
48484f52095Schristos DPRINTF("next_ephemeral initially: %u\n", *next_ephemeral);
48584f52095Schristos
48684f52095Schristos count = num_ephemeral;
48784f52095Schristos
48884f52095Schristos do {
4890e390eeeSozaki-r if (check_suitable_port(*next_ephemeral, inp, cred)) {
49084f52095Schristos *port = *next_ephemeral;
49184f52095Schristos DPRINTF("%s returning port %d\n", __func__, *port);
49284f52095Schristos return 0;
49384f52095Schristos }
49484f52095Schristos *next_ephemeral = mymin +
49584f52095Schristos (cprng_fast32() % num_ephemeral);
49684f52095Schristos
49784f52095Schristos count--;
49884f52095Schristos
49984f52095Schristos DPRINTF("next_ephemeral: %u count: %u\n",
50084f52095Schristos *next_ephemeral, count);
50184f52095Schristos } while (count > 0);
50284f52095Schristos
50384f52095Schristos DPRINTF("%s returning EINVAL\n", __func__);
50484f52095Schristos
50584f52095Schristos return EINVAL;
50684f52095Schristos }
50784f52095Schristos
50884f52095Schristos /* This is the implementation from FreeBSD, with tweaks */
50984f52095Schristos static uint16_t
Fhash(const struct inpcb * inp)5100e390eeeSozaki-r Fhash(const struct inpcb *inp)
51184f52095Schristos {
51284f52095Schristos MD5_CTX f_ctx;
51384f52095Schristos uint32_t Ff[4];
51484f52095Schristos uint32_t secret_f[4];
51584f52095Schristos uint32_t offset;
51684f52095Schristos uint16_t soffset[2];
51784f52095Schristos
51884f52095Schristos cprng_fast(secret_f, sizeof(secret_f));
51984f52095Schristos
52084f52095Schristos MD5Init(&f_ctx);
5210e390eeeSozaki-r switch (inp->inp_af) {
52284f52095Schristos #ifdef INET
52384f52095Schristos case AF_INET: {
524a071c829Sozaki-r MD5Update(&f_ctx, (const u_char *)&const_in4p_laddr(inp),
525a071c829Sozaki-r sizeof(const_in4p_laddr(inp)));
526a071c829Sozaki-r MD5Update(&f_ctx, (const u_char *)&const_in4p_faddr(inp),
527a071c829Sozaki-r sizeof(const_in4p_faddr(inp)));
52884f52095Schristos MD5Update(&f_ctx, (const u_char *)&inp->inp_fport,
52984f52095Schristos sizeof(inp->inp_fport));
53084f52095Schristos break;
53184f52095Schristos }
53284f52095Schristos #endif
53384f52095Schristos #ifdef INET6
53484f52095Schristos case AF_INET6: {
535a071c829Sozaki-r MD5Update(&f_ctx, (const u_char *)&const_in6p_laddr(inp),
536a071c829Sozaki-r sizeof(const_in6p_laddr(inp)));
537a071c829Sozaki-r MD5Update(&f_ctx, (const u_char *)&const_in6p_faddr(inp),
538a071c829Sozaki-r sizeof(const_in6p_faddr(inp)));
5390e390eeeSozaki-r MD5Update(&f_ctx, (const u_char *)&inp->inp_fport,
5400e390eeeSozaki-r sizeof(inp->inp_fport));
54184f52095Schristos break;
54284f52095Schristos }
54384f52095Schristos #endif
54484f52095Schristos default:
54584f52095Schristos break;
54684f52095Schristos }
54784f52095Schristos MD5Update(&f_ctx, (const u_char *)secret_f, sizeof(secret_f));
54884f52095Schristos MD5Final((u_char *)&Ff, &f_ctx);
54984f52095Schristos
55084f52095Schristos offset = (Ff[0] ^ Ff[1]) ^ (Ff[2] ^ Ff[3]);
55184f52095Schristos
55284f52095Schristos memcpy(&soffset, &offset, sizeof(soffset));
55384f52095Schristos
55484f52095Schristos return soffset[0] ^ soffset[1];
55584f52095Schristos }
55684f52095Schristos
55784f52095Schristos /*
55884f52095Schristos * Checks whether the tuple is complete. If not, marks the pcb for
55984f52095Schristos * late binding.
56084f52095Schristos */
56184f52095Schristos static bool
iscompletetuple(struct inpcb * inp)5620e390eeeSozaki-r iscompletetuple(struct inpcb *inp)
56384f52095Schristos {
56484f52095Schristos
5650e390eeeSozaki-r switch (inp->inp_af) {
56684f52095Schristos #ifdef INET
56784f52095Schristos case AF_INET: {
568a071c829Sozaki-r if (inp->inp_fport == 0 || in_nullhost(in4p_faddr(inp))) {
56984f52095Schristos DPRINTF("%s fport or faddr missing, delaying port "
57084f52095Schristos "to connect/send\n", __func__);
57184f52095Schristos inp->inp_bindportonsend = true;
57284f52095Schristos return false;
57384f52095Schristos } else {
57484f52095Schristos inp->inp_bindportonsend = false;
57584f52095Schristos }
57684f52095Schristos break;
57784f52095Schristos }
57884f52095Schristos #endif
57984f52095Schristos #ifdef INET6
58084f52095Schristos case AF_INET6: {
581a071c829Sozaki-r if (inp->inp_fport == 0 || memcmp(&in6p_faddr(inp),
582a071c829Sozaki-r &in6addr_any, sizeof(in6p_faddr(inp))) == 0) {
58384f52095Schristos DPRINTF("%s fport or faddr missing, delaying port "
58484f52095Schristos "to connect/send\n", __func__);
5850e390eeeSozaki-r inp->inp_bindportonsend = true;
58684f52095Schristos return false;
58784f52095Schristos } else {
5880e390eeeSozaki-r inp->inp_bindportonsend = false;
58984f52095Schristos }
59084f52095Schristos break;
59184f52095Schristos }
59284f52095Schristos #endif
59384f52095Schristos default:
59484f52095Schristos DPRINTF("%s incorrect address family\n", __func__);
59584f52095Schristos return false;
59684f52095Schristos }
59784f52095Schristos
59884f52095Schristos return true;
59984f52095Schristos }
60084f52095Schristos
60184f52095Schristos static int
algo_hash(int algo,uint16_t * port,struct inpcb * inp,kauth_cred_t cred)6020e390eeeSozaki-r algo_hash(int algo, uint16_t *port, struct inpcb *inp,
60384f52095Schristos kauth_cred_t cred)
60484f52095Schristos {
60584f52095Schristos uint16_t count, num_ephemeral;
60684f52095Schristos uint16_t mymin, mymax, lastport;
60784f52095Schristos uint16_t *next_ephemeral;
60884f52095Schristos uint16_t offset, myport;
60984f52095Schristos int error;
61084f52095Schristos
61184f52095Schristos DPRINTF("%s called\n", __func__);
61284f52095Schristos
6130e390eeeSozaki-r error = pcb_getports(inp, &lastport, &mymin, &mymax,
61484f52095Schristos &next_ephemeral, algo);
61584f52095Schristos if (error)
61684f52095Schristos return error;
61784f52095Schristos
6180e390eeeSozaki-r if (!iscompletetuple(inp)) {
61984f52095Schristos *port = 0;
62084f52095Schristos return 0;
62184f52095Schristos }
62284f52095Schristos
62384f52095Schristos /* Ephemeral port selection function */
62484f52095Schristos num_ephemeral = mymax - mymin + 1;
62584f52095Schristos
62684f52095Schristos DPRINTF("num_ephemeral: %d\n", num_ephemeral);
62784f52095Schristos
6280e390eeeSozaki-r offset = Fhash(inp);
62984f52095Schristos
63084f52095Schristos count = num_ephemeral;
63184f52095Schristos do {
63284f52095Schristos myport = mymin + (*next_ephemeral + offset)
63384f52095Schristos % num_ephemeral;
63484f52095Schristos
63584f52095Schristos (*next_ephemeral)++;
63684f52095Schristos
6370e390eeeSozaki-r if (check_suitable_port(myport, inp, cred)) {
63884f52095Schristos *port = myport;
63984f52095Schristos DPRINTF("%s returning port %d\n", __func__, *port);
64084f52095Schristos return 0;
64184f52095Schristos }
64284f52095Schristos count--;
64384f52095Schristos } while (count > 0);
64484f52095Schristos
64584f52095Schristos DPRINTF("%s returning EINVAL\n", __func__);
64684f52095Schristos
64784f52095Schristos return EINVAL;
64884f52095Schristos }
64984f52095Schristos
65084f52095Schristos static int
algo_doublehash(int algo,uint16_t * port,struct inpcb * inp,kauth_cred_t cred)6510e390eeeSozaki-r algo_doublehash(int algo, uint16_t *port, struct inpcb *inp,
65284f52095Schristos kauth_cred_t cred)
65384f52095Schristos {
65484f52095Schristos uint16_t count, num_ephemeral;
65584f52095Schristos uint16_t mymin, mymax, lastport;
65684f52095Schristos uint16_t *next_ephemeral;
65784f52095Schristos uint16_t offset, myport;
65884f52095Schristos static uint16_t dhtable[8];
65984f52095Schristos size_t idx;
66084f52095Schristos int error;
66184f52095Schristos
66284f52095Schristos DPRINTF("%s called\n", __func__);
66384f52095Schristos
6640e390eeeSozaki-r error = pcb_getports(inp, &lastport, &mymin, &mymax,
66584f52095Schristos &next_ephemeral, algo);
66684f52095Schristos if (error)
66784f52095Schristos return error;
66884f52095Schristos
6690e390eeeSozaki-r if (!iscompletetuple(inp)) {
67084f52095Schristos *port = 0;
67184f52095Schristos return 0;
67284f52095Schristos }
67384f52095Schristos /* first time initialization */
67484f52095Schristos if (dhtable[0] == 0)
67584f52095Schristos for (size_t i = 0; i < __arraycount(dhtable); i++)
6768403248fSjoerg dhtable[i] = cprng_fast32() & 0xffff;
67784f52095Schristos
67884f52095Schristos /* Ephemeral port selection function */
67984f52095Schristos num_ephemeral = mymax - mymin + 1;
6800e390eeeSozaki-r offset = Fhash(inp);
6810e390eeeSozaki-r idx = Fhash(inp) % __arraycount(dhtable); /* G */
68284f52095Schristos count = num_ephemeral;
68384f52095Schristos
68484f52095Schristos do {
68584f52095Schristos myport = mymin + (offset + dhtable[idx])
68684f52095Schristos % num_ephemeral;
68784f52095Schristos dhtable[idx]++;
68884f52095Schristos
6890e390eeeSozaki-r if (check_suitable_port(myport, inp, cred)) {
69084f52095Schristos *port = myport;
69184f52095Schristos DPRINTF("%s returning port %d\n", __func__, *port);
69284f52095Schristos return 0;
69384f52095Schristos }
69484f52095Schristos count--;
69584f52095Schristos
69684f52095Schristos } while (count > 0);
69784f52095Schristos
69884f52095Schristos DPRINTF("%s returning EINVAL\n", __func__);
69984f52095Schristos
70084f52095Schristos return EINVAL;
70184f52095Schristos }
70284f52095Schristos
70384f52095Schristos static int
algo_randinc(int algo,uint16_t * port,struct inpcb * inp,kauth_cred_t cred)7040e390eeeSozaki-r algo_randinc(int algo, uint16_t *port, struct inpcb *inp,
70584f52095Schristos kauth_cred_t cred)
70684f52095Schristos {
70784f52095Schristos static const uint16_t N = 500; /* Determines the trade-off */
70884f52095Schristos uint16_t count, num_ephemeral;
70984f52095Schristos uint16_t mymin, mymax, lastport;
71084f52095Schristos uint16_t *next_ephemeral;
71184f52095Schristos uint16_t myport;
71284f52095Schristos int error;
71384f52095Schristos
71484f52095Schristos DPRINTF("%s called\n", __func__);
71584f52095Schristos
7160e390eeeSozaki-r error = pcb_getports(inp, &lastport, &mymin, &mymax,
71784f52095Schristos &next_ephemeral, algo);
71884f52095Schristos if (error)
71984f52095Schristos return error;
72084f52095Schristos
72184f52095Schristos if (*next_ephemeral == 0)
72284f52095Schristos *next_ephemeral = cprng_fast32() & 0xffff;
72384f52095Schristos
72484f52095Schristos /* Ephemeral port selection function */
72584f52095Schristos num_ephemeral = mymax - mymin + 1;
72684f52095Schristos
72784f52095Schristos count = num_ephemeral;
72884f52095Schristos do {
72984f52095Schristos *next_ephemeral = *next_ephemeral +
73084f52095Schristos (cprng_fast32() % N) + 1;
73184f52095Schristos myport = mymin +
73284f52095Schristos (*next_ephemeral % num_ephemeral);
73384f52095Schristos
7340e390eeeSozaki-r if (check_suitable_port(myport, inp, cred)) {
73584f52095Schristos *port = myport;
73684f52095Schristos DPRINTF("%s returning port %d\n", __func__, *port);
73784f52095Schristos return 0;
73884f52095Schristos }
73984f52095Schristos count--;
74084f52095Schristos } while (count > 0);
74184f52095Schristos
74284f52095Schristos return EINVAL;
74384f52095Schristos }
74484f52095Schristos
74584f52095Schristos /* The generic function called in order to pick a port. */
74684f52095Schristos int
portalgo_randport(uint16_t * port,struct inpcb * inp,kauth_cred_t cred)7470e390eeeSozaki-r portalgo_randport(uint16_t *port, struct inpcb *inp, kauth_cred_t cred)
74884f52095Schristos {
74984f52095Schristos int algo, error;
75084f52095Schristos uint16_t lport;
75184f52095Schristos int default_algo;
75284f52095Schristos
75384f52095Schristos DPRINTF("%s called\n", __func__);
75484f52095Schristos
7550e390eeeSozaki-r if (inp->inp_portalgo == PORTALGO_DEFAULT) {
7560e390eeeSozaki-r switch (inp->inp_af) {
75784f52095Schristos #ifdef INET
75884f52095Schristos case AF_INET:
75984f52095Schristos default_algo = inet4_portalgo;
76084f52095Schristos break;
76184f52095Schristos #endif
76284f52095Schristos #ifdef INET6
76384f52095Schristos case AF_INET6:
76484f52095Schristos default_algo = inet6_portalgo;
76584f52095Schristos break;
76684f52095Schristos #endif
76784f52095Schristos default:
76884f52095Schristos return EINVAL;
76984f52095Schristos }
77084f52095Schristos
77184f52095Schristos if (default_algo == PORTALGO_DEFAULT)
77284f52095Schristos algo = PORTALGO_BSD;
77384f52095Schristos else
77484f52095Schristos algo = default_algo;
77584f52095Schristos }
77684f52095Schristos else /* socket specifies the algorithm */
7770e390eeeSozaki-r algo = inp->inp_portalgo;
77884f52095Schristos
77984f52095Schristos KASSERT(algo >= 0);
78084f52095Schristos KASSERT(algo < NALGOS);
78184f52095Schristos
7820e390eeeSozaki-r switch (inp->inp_af) {
78384f52095Schristos #ifdef INET
78484f52095Schristos case AF_INET: {
785f89df58bSchristos char buf[INET_ADDRSTRLEN];
786a071c829Sozaki-r DPRINTF("local addr: %s\n", IN_PRINT(buf, &in4p_laddr(inp)));
78784f52095Schristos DPRINTF("local port: %d\n", inp->inp_lport);
788a071c829Sozaki-r DPRINTF("foreign addr: %s\n", IN_PRINT(buf, &in4p_faddr(inp)));
78984f52095Schristos DPRINTF("foreign port: %d\n", inp->inp_fport);
79084f52095Schristos break;
79184f52095Schristos }
79284f52095Schristos #endif
79384f52095Schristos #ifdef INET6
79484f52095Schristos case AF_INET6: {
795f89df58bSchristos char buf[INET6_ADDRSTRLEN];
796a071c829Sozaki-r DPRINTF("local addr: %s\n", IN6_PRINT(buf, &in6p_laddr(inp)));
7970e390eeeSozaki-r DPRINTF("local port: %d\n", inp->inp_lport);
798f89df58bSchristos DPRINTF("foreign addr: %s\n", IN6_PRINT(buf,
799a071c829Sozaki-r &in6p_laddr(inp)));
8000e390eeeSozaki-r DPRINTF("foreign port: %d\n", inp->inp_fport);
80184f52095Schristos break;
80284f52095Schristos }
80384f52095Schristos #endif
80484f52095Schristos default:
80584f52095Schristos break;
80684f52095Schristos }
80784f52095Schristos
80884f52095Schristos DPRINTF("%s portalgo = %d\n", __func__, algo);
80984f52095Schristos
8100e390eeeSozaki-r error = (*algos[algo].func)(algo, &lport, inp, cred);
81184f52095Schristos if (error == 0) {
81284f52095Schristos *port = lport;
81384f52095Schristos } else if (error != EAGAIN) {
81484f52095Schristos uint16_t lastport, mymin, mymax, *pnext_ephemeral;
81584f52095Schristos
8160e390eeeSozaki-r error = pcb_getports(inp, &lastport, &mymin,
81784f52095Schristos &mymax, &pnext_ephemeral, algo);
81884f52095Schristos if (error)
81984f52095Schristos return error;
82084f52095Schristos *port = lastport - 1;
82184f52095Schristos }
82284f52095Schristos return error;
82384f52095Schristos }
82484f52095Schristos
82584f52095Schristos /* Sets the algorithm to be used globally */
82684f52095Schristos static int
portalgo_algo_name_select(const char * name,int * algo)82784f52095Schristos portalgo_algo_name_select(const char *name, int *algo)
82884f52095Schristos {
82984f52095Schristos size_t ai;
83084f52095Schristos
83184f52095Schristos DPRINTF("%s called\n", __func__);
83284f52095Schristos
83384f52095Schristos for (ai = 0; ai < NALGOS; ai++)
83484f52095Schristos if (strcmp(algos[ai].name, name) == 0) {
83584f52095Schristos DPRINTF("%s: found idx %zu\n", __func__, ai);
83684f52095Schristos *algo = ai;
83784f52095Schristos return 0;
83884f52095Schristos }
83984f52095Schristos return EINVAL;
84084f52095Schristos }
84184f52095Schristos
84284f52095Schristos /* Sets the algorithm to be used by the pcb inp. */
84384f52095Schristos int
portalgo_algo_index_select(struct inpcb * inp,int algo)8440e390eeeSozaki-r portalgo_algo_index_select(struct inpcb *inp, int algo)
84584f52095Schristos {
84684f52095Schristos
84784f52095Schristos DPRINTF("%s called with algo %d for pcb %p\n", __func__, algo, inp );
84884f52095Schristos
84984f52095Schristos if ((algo < 0 || algo >= NALGOS) &&
85084f52095Schristos (algo != PORTALGO_DEFAULT))
85184f52095Schristos return EINVAL;
85284f52095Schristos
8530e390eeeSozaki-r inp->inp_portalgo = algo;
85484f52095Schristos return 0;
85584f52095Schristos }
85684f52095Schristos
85784f52095Schristos /*
85884f52095Schristos * The sysctl hook that is supposed to check that we are picking one
859cbf1f72bSchristos * of the valid algorithms.
86084f52095Schristos */
86184f52095Schristos static int
sysctl_portalgo_selected(SYSCTLFN_ARGS,int * algo)862cbf1f72bSchristos sysctl_portalgo_selected(SYSCTLFN_ARGS, int *algo)
86384f52095Schristos {
86484f52095Schristos struct sysctlnode node;
86584f52095Schristos int error;
86684f52095Schristos char newalgo[PORTALGO_MAXLEN];
86784f52095Schristos
86884f52095Schristos DPRINTF("%s called\n", __func__);
86984f52095Schristos
87084f52095Schristos strlcpy(newalgo, algos[*algo].name, sizeof(newalgo));
87184f52095Schristos
87284f52095Schristos node = *rnode;
87384f52095Schristos node.sysctl_data = newalgo;
87484f52095Schristos node.sysctl_size = sizeof(newalgo);
87584f52095Schristos
87684f52095Schristos error = sysctl_lookup(SYSCTLFN_CALL(&node));
87784f52095Schristos
87884f52095Schristos DPRINTF("newalgo: %s\n", newalgo);
87984f52095Schristos
88084f52095Schristos if (error || newp == NULL ||
88184f52095Schristos strncmp(newalgo, algos[*algo].name, sizeof(newalgo)) == 0)
88284f52095Schristos return error;
88384f52095Schristos
88484f52095Schristos #ifdef KAUTH_NETWORK_SOCKET_PORT_RANDOMIZE
88584f52095Schristos if (l != NULL && (error = kauth_authorize_system(l->l_cred,
88684f52095Schristos KAUTH_NETWORK_SOCKET, KAUTH_NETWORK_SOCKET_PORT_RANDOMIZE, newname,
88784f52095Schristos NULL, NULL)) != 0)
88884f52095Schristos return error;
88984f52095Schristos #endif
89084f52095Schristos
89184f52095Schristos mutex_enter(softnet_lock);
89284f52095Schristos error = portalgo_algo_name_select(newalgo, algo);
89384f52095Schristos mutex_exit(softnet_lock);
89484f52095Schristos return error;
89584f52095Schristos }
89684f52095Schristos
897cbf1f72bSchristos static int
sysctl_portalgo_reserve(SYSCTLFN_ARGS,bitmap * bt)8989878d5e5Schristos sysctl_portalgo_reserve(SYSCTLFN_ARGS, bitmap *bt)
899cbf1f72bSchristos {
900cbf1f72bSchristos struct sysctlnode node;
901cbf1f72bSchristos int error;
902cbf1f72bSchristos
903cbf1f72bSchristos DPRINTF("%s called\n", __func__);
904cbf1f72bSchristos
905cbf1f72bSchristos node = *rnode;
906cbf1f72bSchristos node.sysctl_data = bt;
907cbf1f72bSchristos node.sysctl_size = sizeof(*bt);
908cbf1f72bSchristos
909cbf1f72bSchristos error = sysctl_lookup(SYSCTLFN_CALL(&node));
910cbf1f72bSchristos
911cbf1f72bSchristos if (error || newp == NULL)
912cbf1f72bSchristos return error;
913cbf1f72bSchristos
914cbf1f72bSchristos #ifdef KAUTH_NETWORK_SOCKET_PORT_RESERVE
915cbf1f72bSchristos if (l != NULL && (error = kauth_authorize_system(l->l_cred,
916cbf1f72bSchristos KAUTH_NETWORK_SOCKET, KAUTH_NETWORK_SOCKET_PORT_RESERVE, bt,
917cbf1f72bSchristos NULL, NULL)) != 0)
918cbf1f72bSchristos return error;
919cbf1f72bSchristos #endif
920cbf1f72bSchristos return error;
921cbf1f72bSchristos }
922cbf1f72bSchristos
923cbf1f72bSchristos #ifdef INET
92484f52095Schristos /*
92584f52095Schristos * The sysctl hook that is supposed to check that we are picking one
92684f52095Schristos * of the valid algorithms.
92784f52095Schristos */
92884f52095Schristos int
sysctl_portalgo_selected4(SYSCTLFN_ARGS)929cbf1f72bSchristos sysctl_portalgo_selected4(SYSCTLFN_ARGS)
93084f52095Schristos {
93184f52095Schristos
932cbf1f72bSchristos return sysctl_portalgo_selected(SYSCTLFN_CALL(rnode), &inet4_portalgo);
93384f52095Schristos }
93484f52095Schristos
935cbf1f72bSchristos int
sysctl_portalgo_reserve4(SYSCTLFN_ARGS)936cbf1f72bSchristos sysctl_portalgo_reserve4(SYSCTLFN_ARGS)
937cbf1f72bSchristos {
938cbf1f72bSchristos
9399878d5e5Schristos return sysctl_portalgo_reserve(SYSCTLFN_CALL(rnode), &inet4_reserve);
940cbf1f72bSchristos }
941cbf1f72bSchristos #endif
942cbf1f72bSchristos
94384f52095Schristos #ifdef INET6
94484f52095Schristos int
sysctl_portalgo_selected6(SYSCTLFN_ARGS)94584f52095Schristos sysctl_portalgo_selected6(SYSCTLFN_ARGS)
94684f52095Schristos {
94784f52095Schristos
948cbf1f72bSchristos return sysctl_portalgo_selected(SYSCTLFN_CALL(rnode), &inet6_portalgo);
949cbf1f72bSchristos }
950cbf1f72bSchristos
951cbf1f72bSchristos int
sysctl_portalgo_reserve6(SYSCTLFN_ARGS)952cbf1f72bSchristos sysctl_portalgo_reserve6(SYSCTLFN_ARGS)
953cbf1f72bSchristos {
9549878d5e5Schristos return sysctl_portalgo_reserve(SYSCTLFN_CALL(rnode), &inet6_reserve);
95584f52095Schristos }
95684f52095Schristos #endif
95784f52095Schristos
95884f52095Schristos /*
95984f52095Schristos * The sysctl hook that returns the available
96084f52095Schristos * algorithms.
96184f52095Schristos */
96284f52095Schristos int
sysctl_portalgo_available(SYSCTLFN_ARGS)96384f52095Schristos sysctl_portalgo_available(SYSCTLFN_ARGS)
96484f52095Schristos {
96584f52095Schristos size_t ai, len = 0;
96684f52095Schristos struct sysctlnode node;
96784f52095Schristos char availalgo[NALGOS * PORTALGO_MAXLEN];
96884f52095Schristos
96984f52095Schristos DPRINTF("%s called\n", __func__);
97084f52095Schristos
97184f52095Schristos availalgo[0] = '\0';
97284f52095Schristos
97384f52095Schristos for (ai = 0; ai < NALGOS; ai++) {
97484f52095Schristos len = strlcat(availalgo, algos[ai].name, sizeof(availalgo));
97584f52095Schristos if (ai < NALGOS - 1)
97684f52095Schristos strlcat(availalgo, " ", sizeof(availalgo));
97784f52095Schristos }
97884f52095Schristos
97984f52095Schristos DPRINTF("available algos: %s\n", availalgo);
98084f52095Schristos
98184f52095Schristos node = *rnode;
98284f52095Schristos node.sysctl_data = availalgo;
98384f52095Schristos node.sysctl_size = len;
98484f52095Schristos
98584f52095Schristos return sysctl_lookup(SYSCTLFN_CALL(&node));
98684f52095Schristos }
987