Allow the kernel to provide random source ports.

On some operating systems, for example OpenBSD since some decades, the
kernel binds to a random source port if asked for any port (port
number 0). There is no need to replicate this functionality in
userland.
This commit is contained in:
Florian Obser 2020-01-10 13:55:55 +01:00
parent d2a843b422
commit 5aaa5e253d
5 changed files with 56 additions and 2 deletions

View file

@ -805,6 +805,10 @@
/* Define to 1 to use ipset support */ /* Define to 1 to use ipset support */
#undef USE_IPSET #undef USE_IPSET
/* Define to 1 to disable explict UDP source port randomisation and rely on the
kernel to provide random source ports */
#undef DISABLE_EXPLICIT_PORT_RANDOMISATION
/* Define if you want to use internal select based events */ /* Define if you want to use internal select based events */
#undef USE_MINI_EVENT #undef USE_MINI_EVENT

19
configure vendored
View file

@ -890,6 +890,7 @@ enable_cachedb
enable_ipsecmod enable_ipsecmod
enable_ipset enable_ipset
with_libmnl with_libmnl
enable_explicit_port_randomisation
with_libunbound_only with_libunbound_only
' '
ac_precious_vars='build_alias ac_precious_vars='build_alias
@ -1579,6 +1580,9 @@ Optional Features:
--enable-ipsecmod Enable ipsecmod module that facilitates --enable-ipsecmod Enable ipsecmod module that facilitates
opportunistic IPsec opportunistic IPsec
--enable-ipset enable ipset module --enable-ipset enable ipset module
--disable-explicit-port-randomisation
disable explicit source port randomisation and rely
on the kernel to provide random source ports
Optional Packages: Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@ -21443,6 +21447,21 @@ $as_echo "found in $dir" >&6; }
# nothing # nothing
;; ;;
esac esac
# Check whether --enable-explicit-port-randomisation was given.
if test "${enable_explicit_port_randomisation+set}" = set; then :
enableval=$enable_explicit_port_randomisation;
fi
case "$enable_explicit_port_randomisation" in
no)
$as_echo "#define DISABLE_EXPLICIT_PORT_RANDOMISATION 1" >>confdefs.h
;;
yes|*)
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if ${MAKE:-make} supports $< with implicit rule in scope" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if ${MAKE:-make} supports $< with implicit rule in scope" >&5
$as_echo_n "checking if ${MAKE:-make} supports $< with implicit rule in scope... " >&6; } $as_echo_n "checking if ${MAKE:-make} supports $< with implicit rule in scope... " >&6; }

View file

@ -1778,6 +1778,15 @@ case "$enable_ipset" in
# nothing # nothing
;; ;;
esac esac
AC_ARG_ENABLE(explicit-port-randomisation, AC_HELP_STRING([--disable-explicit-port-randomisation], [disable explicit source port randomisation and rely on the kernel to provide random source ports]))
case "$enable_explicit_port_randomisation" in
no)
AC_DEFINE([DISABLE_EXPLICIT_PORT_RANDOMISATION], [1], [Define this to enable kernel based UDP source port randomization.])
;;
yes|*)
;;
esac
AC_MSG_CHECKING([if ${MAKE:-make} supports $< with implicit rule in scope]) AC_MSG_CHECKING([if ${MAKE:-make} supports $< with implicit rule in scope])
# on openBSD, the implicit rule make $< work. # on openBSD, the implicit rule make $< work.

View file

@ -514,7 +514,9 @@ portcomm_loweruse(struct outside_network* outnet, struct port_comm* pc)
comm_point_close(pc->cp); comm_point_close(pc->cp);
pif = pc->pif; pif = pc->pif;
log_assert(pif->inuse > 0); log_assert(pif->inuse > 0);
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
pif->avail_ports[pif->avail_total - pif->inuse] = pc->number; pif->avail_ports[pif->avail_total - pif->inuse] = pc->number;
#endif
pif->inuse--; pif->inuse--;
pif->out[pc->index] = pif->out[pif->inuse]; pif->out[pc->index] = pif->out[pif->inuse];
pif->out[pc->index]->index = pc->index; pif->out[pc->index]->index = pc->index;
@ -727,10 +729,12 @@ create_pending_tcp(struct outside_network* outnet, size_t bufsize)
static int setup_if(struct port_if* pif, const char* addrstr, static int setup_if(struct port_if* pif, const char* addrstr,
int* avail, int numavail, size_t numfd) int* avail, int numavail, size_t numfd)
{ {
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
pif->avail_total = numavail; pif->avail_total = numavail;
pif->avail_ports = (int*)memdup(avail, (size_t)numavail*sizeof(int)); pif->avail_ports = (int*)memdup(avail, (size_t)numavail*sizeof(int));
if(!pif->avail_ports) if(!pif->avail_ports)
return 0; return 0;
#endif
if(!ipstrtoaddr(addrstr, UNBOUND_DNS_PORT, &pif->addr, &pif->addrlen) && if(!ipstrtoaddr(addrstr, UNBOUND_DNS_PORT, &pif->addr, &pif->addrlen) &&
!netblockstrtoaddr(addrstr, UNBOUND_DNS_PORT, !netblockstrtoaddr(addrstr, UNBOUND_DNS_PORT,
&pif->addr, &pif->addrlen, &pif->pfxlen)) &pif->addr, &pif->addrlen, &pif->pfxlen))
@ -957,7 +961,9 @@ outside_network_delete(struct outside_network* outnet)
comm_point_delete(pc->cp); comm_point_delete(pc->cp);
free(pc); free(pc);
} }
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
free(outnet->ip4_ifs[i].avail_ports); free(outnet->ip4_ifs[i].avail_ports);
#endif
free(outnet->ip4_ifs[i].out); free(outnet->ip4_ifs[i].out);
} }
free(outnet->ip4_ifs); free(outnet->ip4_ifs);
@ -971,7 +977,9 @@ outside_network_delete(struct outside_network* outnet)
comm_point_delete(pc->cp); comm_point_delete(pc->cp);
free(pc); free(pc);
} }
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
free(outnet->ip6_ifs[i].avail_ports); free(outnet->ip6_ifs[i].avail_ports);
#endif
free(outnet->ip6_ifs[i].out); free(outnet->ip6_ifs[i].out);
} }
free(outnet->ip6_ifs); free(outnet->ip6_ifs);
@ -1135,6 +1143,7 @@ select_ifport(struct outside_network* outnet, struct pending* pend,
while(1) { while(1) {
my_if = ub_random_max(outnet->rnd, num_if); my_if = ub_random_max(outnet->rnd, num_if);
pif = &ifs[my_if]; pif = &ifs[my_if];
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
my_port = ub_random_max(outnet->rnd, pif->avail_total); my_port = ub_random_max(outnet->rnd, pif->avail_total);
if(my_port < pif->inuse) { if(my_port < pif->inuse) {
/* port already open */ /* port already open */
@ -1146,6 +1155,9 @@ select_ifport(struct outside_network* outnet, struct pending* pend,
/* try to open new port, if fails, loop to try again */ /* try to open new port, if fails, loop to try again */
log_assert(pif->inuse < pif->maxout); log_assert(pif->inuse < pif->maxout);
portno = pif->avail_ports[my_port - pif->inuse]; portno = pif->avail_ports[my_port - pif->inuse];
#else
my_port = portno = 0;
#endif
fd = udp_sockport(&pif->addr, pif->addrlen, pif->pfxlen, fd = udp_sockport(&pif->addr, pif->addrlen, pif->pfxlen,
portno, &inuse, outnet->rnd); portno, &inuse, outnet->rnd);
if(fd == -1 && !inuse) { if(fd == -1 && !inuse) {
@ -1169,8 +1181,10 @@ select_ifport(struct outside_network* outnet, struct pending* pend,
/* grab port in interface */ /* grab port in interface */
pif->out[pif->inuse] = pend->pc; pif->out[pif->inuse] = pend->pc;
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
pif->avail_ports[my_port - pif->inuse] = pif->avail_ports[my_port - pif->inuse] =
pif->avail_ports[pif->avail_total-pif->inuse-1]; pif->avail_ports[pif->avail_total-pif->inuse-1];
#endif
pif->inuse++; pif->inuse++;
break; break;
} }
@ -2227,6 +2241,7 @@ fd_for_dest(struct outside_network* outnet, struct sockaddr_storage* to_addr,
} }
addr = &pif->addr; addr = &pif->addr;
addrlen = pif->addrlen; addrlen = pif->addrlen;
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
pnum = ub_random_max(outnet->rnd, pif->avail_total); pnum = ub_random_max(outnet->rnd, pif->avail_total);
if(pnum < pif->inuse) { if(pnum < pif->inuse) {
/* port already open */ /* port already open */
@ -2235,7 +2250,9 @@ fd_for_dest(struct outside_network* outnet, struct sockaddr_storage* to_addr,
/* unused ports in start part of array */ /* unused ports in start part of array */
port = pif->avail_ports[pnum - pif->inuse]; port = pif->avail_ports[pnum - pif->inuse];
} }
#else
pnum = port = 0;
#endif
if(addr_is_ip6(to_addr, to_addrlen)) { if(addr_is_ip6(to_addr, to_addrlen)) {
struct sockaddr_in6 sa = *(struct sockaddr_in6*)addr; struct sockaddr_in6 sa = *(struct sockaddr_in6*)addr;
sa.sin6_port = (in_port_t)htons((uint16_t)port); sa.sin6_port = (in_port_t)htons((uint16_t)port);
@ -2459,7 +2476,10 @@ if_get_mem(struct port_if* pif)
{ {
size_t s; size_t s;
int i; int i;
s = sizeof(*pif) + sizeof(int)*pif->avail_total + s = sizeof(*pif) +
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
sizeof(int)*pif->avail_total +
#endif
sizeof(struct port_comm*)*pif->maxout; sizeof(struct port_comm*)*pif->maxout;
for(i=0; i<pif->inuse; i++) for(i=0; i<pif->inuse; i++)
s += sizeof(*pif->out[i]) + s += sizeof(*pif->out[i]) +

View file

@ -172,11 +172,13 @@ struct port_if {
* if 0, no randomisation. */ * if 0, no randomisation. */
int pfxlen; int pfxlen;
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
/** the available ports array. These are unused. /** the available ports array. These are unused.
* Only the first total-inuse part is filled. */ * Only the first total-inuse part is filled. */
int* avail_ports; int* avail_ports;
/** the total number of available ports (size of the array) */ /** the total number of available ports (size of the array) */
int avail_total; int avail_total;
#endif
/** array of the commpoints currently in use. /** array of the commpoints currently in use.
* allocated for max number of fds, first part in use. */ * allocated for max number of fds, first part in use. */