diff --git a/config.h.in b/config.h.in index daad19726..3c46e3f3e 100644 --- a/config.h.in +++ b/config.h.in @@ -811,6 +811,10 @@ /* Define to 1 to use ipset support */ #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 */ #undef USE_MINI_EVENT diff --git a/configure b/configure index cabfd55e1..d9355009c 100755 --- a/configure +++ b/configure @@ -890,6 +890,7 @@ enable_cachedb enable_ipsecmod enable_ipset with_libmnl +enable_explicit_port_randomisation with_libunbound_only ' ac_precious_vars='build_alias @@ -1579,6 +1580,9 @@ Optional Features: --enable-ipsecmod Enable ipsecmod module that facilitates opportunistic IPsec --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: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -21391,6 +21395,21 @@ $as_echo "found in $dir" >&6; } # nothing ;; 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_n "checking if ${MAKE:-make} supports $< with implicit rule in scope... " >&6; } diff --git a/configure.ac b/configure.ac index aa2eb47e1..78f307655 100644 --- a/configure.ac +++ b/configure.ac @@ -1782,6 +1782,15 @@ case "$enable_ipset" in # nothing ;; 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]) # on openBSD, the implicit rule make $< work. diff --git a/doc/Changelog b/doc/Changelog index 2466f16f5..a72ff1b2d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -2,6 +2,10 @@ - Merge PR#191: Update iOS testing on Travis, by Jeffrey Walton. - Fix #158: open tls-session-ticket-keys as binary, for Windows. By Daisuke HIGASHI. + - Merge PR#134, Allow the kernel to provide random source ports. By + Florian Obser. + - Log warning when using outgoing-port-permit and outgoing-port-avoid + while explicit port randomisation is disabled. 16 March 2020: Wouter - Fix #192: In the unbound-checkconf tool, the module config of diff --git a/services/outside_network.c b/services/outside_network.c index 4df00ca7a..b2c67766e 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -482,7 +482,9 @@ portcomm_loweruse(struct outside_network* outnet, struct port_comm* pc) comm_point_close(pc->cp); pif = pc->pif; log_assert(pif->inuse > 0); +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION pif->avail_ports[pif->avail_total - pif->inuse] = pc->number; +#endif pif->inuse--; pif->out[pc->index] = pif->out[pif->inuse]; pif->out[pc->index]->index = pc->index; @@ -695,10 +697,12 @@ create_pending_tcp(struct outside_network* outnet, size_t bufsize) static int setup_if(struct port_if* pif, const char* addrstr, int* avail, int numavail, size_t numfd) { +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION pif->avail_total = numavail; pif->avail_ports = (int*)memdup(avail, (size_t)numavail*sizeof(int)); if(!pif->avail_ports) return 0; +#endif if(!ipstrtoaddr(addrstr, UNBOUND_DNS_PORT, &pif->addr, &pif->addrlen) && !netblockstrtoaddr(addrstr, UNBOUND_DNS_PORT, &pif->addr, &pif->addrlen, &pif->pfxlen)) @@ -925,7 +929,9 @@ outside_network_delete(struct outside_network* outnet) comm_point_delete(pc->cp); free(pc); } +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION free(outnet->ip4_ifs[i].avail_ports); +#endif free(outnet->ip4_ifs[i].out); } free(outnet->ip4_ifs); @@ -939,7 +945,9 @@ outside_network_delete(struct outside_network* outnet) comm_point_delete(pc->cp); free(pc); } +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION free(outnet->ip6_ifs[i].avail_ports); +#endif free(outnet->ip6_ifs[i].out); } free(outnet->ip6_ifs); @@ -1103,6 +1111,7 @@ select_ifport(struct outside_network* outnet, struct pending* pend, while(1) { my_if = ub_random_max(outnet->rnd, num_if); pif = &ifs[my_if]; +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION my_port = ub_random_max(outnet->rnd, pif->avail_total); if(my_port < pif->inuse) { /* port already open */ @@ -1114,6 +1123,9 @@ select_ifport(struct outside_network* outnet, struct pending* pend, /* try to open new port, if fails, loop to try again */ log_assert(pif->inuse < pif->maxout); portno = pif->avail_ports[my_port - pif->inuse]; +#else + my_port = portno = 0; +#endif fd = udp_sockport(&pif->addr, pif->addrlen, pif->pfxlen, portno, &inuse, outnet->rnd); if(fd == -1 && !inuse) { @@ -1137,8 +1149,10 @@ select_ifport(struct outside_network* outnet, struct pending* pend, /* grab port in interface */ pif->out[pif->inuse] = pend->pc; +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION pif->avail_ports[my_port - pif->inuse] = pif->avail_ports[pif->avail_total-pif->inuse-1]; +#endif pif->inuse++; break; } @@ -2195,6 +2209,7 @@ fd_for_dest(struct outside_network* outnet, struct sockaddr_storage* to_addr, } addr = &pif->addr; addrlen = pif->addrlen; +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION pnum = ub_random_max(outnet->rnd, pif->avail_total); if(pnum < pif->inuse) { /* port already open */ @@ -2203,7 +2218,9 @@ fd_for_dest(struct outside_network* outnet, struct sockaddr_storage* to_addr, /* unused ports in start part of array */ port = pif->avail_ports[pnum - pif->inuse]; } - +#else + pnum = port = 0; +#endif if(addr_is_ip6(to_addr, to_addrlen)) { struct sockaddr_in6 sa = *(struct sockaddr_in6*)addr; sa.sin6_port = (in_port_t)htons((uint16_t)port); @@ -2427,7 +2444,10 @@ if_get_mem(struct port_if* pif) { size_t s; 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; for(i=0; iinuse; i++) s += sizeof(*pif->out[i]) + diff --git a/services/outside_network.h b/services/outside_network.h index 3456a3da3..f12b2e5be 100644 --- a/services/outside_network.h +++ b/services/outside_network.h @@ -172,11 +172,13 @@ struct port_if { * if 0, no randomisation. */ int pfxlen; +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION /** the available ports array. These are unused. * Only the first total-inuse part is filled. */ int* avail_ports; /** the total number of available ports (size of the array) */ int avail_total; +#endif /** array of the commpoints currently in use. * allocated for max number of fds, first part in use. */ diff --git a/util/config_file.c b/util/config_file.c index 394cf17f3..814d9f436 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -1530,6 +1530,11 @@ int cfg_mark_ports(const char* str, int allow, int* avail, int num) { char* mid = strchr(str, '-'); +#ifdef DISABLE_EXPLICIT_PORT_RANDOMISATION + log_warn("Explicit port randomisation disabled, ignoring " + "outgoing-port-permit and outgoing-port-avoid configuration " + "options"); +#endif if(!mid) { int port = atoi(str); if(port == 0 && strcmp(str, "0") != 0) {