- Fix #787: outgoing-interface netblock/64 ipv6 option to use linux

freebind to use 64bits of entropy for every query with random local
  part.


git-svn-id: file:///svn/unbound/trunk@3804 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2016-07-04 14:51:30 +00:00
parent b6b3e2b914
commit 1394dcba69
6 changed files with 65 additions and 19 deletions

View File

@ -1,6 +1,9 @@
4 July 2016: Wouter
- For #787: prefer-ip6 option for unbound.conf prefers to send
upstream queries to ipv6 servers.
- Fix #787: outgoing-interface netblock/64 ipv6 option to use linux
freebind to use 64bits of entropy for every query with random local
part.
30 June 2016: Wouter
- Document always_transparent, always_refuse, always_nxdomain types.

View File

@ -127,7 +127,7 @@ Detect source interface on UDP queries and copy them to replies. This
feature is experimental, and needs support in your OS for particular socket
options. Default value is no.
.TP
.B outgoing\-interface: \fI<ip address>
.B outgoing\-interface: \fI<ip address or ip6 netblock>
Interface to use to connect to the network. This interface is used to send
queries to authoritative servers and receive their replies. Can be given
multiple times to work on several interfaces. If none are given the
@ -137,6 +137,18 @@ and
.B outgoing\-interface:
lines, the interfaces are then used for both purposes. Outgoing queries are
sent via a random outgoing interface to counter spoofing.
.IP
If an IPv6 netblock is specified instead of an individual IPv6 address,
outgoing UDP queries will use a randomised source address taken from the
netblock to counter spoofing. Requires the IPv6 netblock to be routed to the
host running unbound, and requires OS support for unprivileged non-local binds
(currently only supported on Linux). Several netblocks may be specified with
multiple
.B outgoing\-interface:
options, but do not specify both an individual IPv6 address and an IPv6
netblock, or the randomisation will be compromised. Consider combining with
.B prefer\-ip6: yes
to increase the likelihood of IPv6 nameservers being selected for queries.
.TP
.B outgoing\-range: \fI<number>
Number of ports to open. This number of file descriptors can be opened per

View File

@ -184,14 +184,6 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
#else
(void)reuseport;
#endif /* defined(SO_REUSEPORT) */
#ifdef IP_FREEBIND
if (freebind &&
setsockopt(s, IPPROTO_IP, IP_FREEBIND, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP_FREEBIND ..) failed: %s",
strerror(errno));
}
#endif /* IP_FREEBIND */
#ifdef IP_TRANSPARENT
if (transparent &&
setsockopt(s, IPPROTO_IP, IP_TRANSPARENT, (void*)&on,
@ -209,6 +201,14 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
}
#endif /* IP_TRANSPARENT || IP_BINDANY */
}
#ifdef IP_FREEBIND
if(freebind &&
setsockopt(s, IPPROTO_IP, IP_FREEBIND, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP_FREEBIND ..) failed: %s",
strerror(errno));
}
#endif /* IP_FREEBIND */
if(rcv) {
#ifdef SO_RCVBUF
int got;

View File

@ -591,7 +591,9 @@ static int setup_if(struct port_if* pif, const char* addrstr,
pif->avail_ports = (int*)memdup(avail, (size_t)numavail*sizeof(int));
if(!pif->avail_ports)
return 0;
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,
&pif->addr, &pif->addrlen, &pif->pfxlen))
return 0;
pif->maxout = (int)numfd;
pif->inuse = 0;
@ -893,26 +895,49 @@ pending_delete(struct outside_network* outnet, struct pending* p)
free(p);
}
static void
sai6_putrandom(struct sockaddr_in6 *sa, int pfxlen, struct ub_randstate *rnd)
{
int i, last;
if(!(pfxlen > 0 && pfxlen < 128))
return;
for(i = 0; i < (128 - pfxlen) / 8; i++) {
sa->sin6_addr.s6_addr[15-i] = ub_random_max(rnd, 256);
}
last = pfxlen & 7;
if(last != 0) {
sa->sin6_addr.s6_addr[15-i] |=
((0xFF >> last) & ub_random_max(rnd, 256));
}
}
/**
* Try to open a UDP socket for outgoing communication.
* Sets sockets options as needed.
* @param addr: socket address.
* @param addrlen: length of address.
* @param pfxlen: length of network prefix (for address randomisation).
* @param port: port override for addr.
* @param inuse: if -1 is returned, this bool means the port was in use.
* @param rnd: random state (for address randomisation).
* @return fd or -1
*/
static int
udp_sockport(struct sockaddr_storage* addr, socklen_t addrlen, int port,
int* inuse)
udp_sockport(struct sockaddr_storage* addr, socklen_t addrlen, int pfxlen,
int port, int* inuse, struct ub_randstate* rnd)
{
int fd, noproto;
if(addr_is_ip6(addr, addrlen)) {
struct sockaddr_in6* sa = (struct sockaddr_in6*)addr;
sa->sin6_port = (in_port_t)htons((uint16_t)port);
int freebind = 0;
struct sockaddr_in6 sa = *(struct sockaddr_in6*)addr;
sa.sin6_port = (in_port_t)htons((uint16_t)port);
if(pfxlen != 0) {
freebind = 1;
sai6_putrandom(&sa, pfxlen, rnd);
}
fd = create_udp_sock(AF_INET6, SOCK_DGRAM,
(struct sockaddr*)addr, addrlen, 1, inuse, &noproto,
0, 0, 0, NULL, 0, 0);
(struct sockaddr*)&sa, addrlen, 1, inuse, &noproto,
0, 0, 0, NULL, 0, freebind);
} else {
struct sockaddr_in* sa = (struct sockaddr_in*)addr;
sa->sin_port = (in_port_t)htons((uint16_t)port);
@ -978,7 +1003,8 @@ 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];
fd = udp_sockport(&pif->addr, pif->addrlen, portno, &inuse);
fd = udp_sockport(&pif->addr, pif->addrlen, pif->pfxlen,
portno, &inuse, outnet->rnd);
if(fd == -1 && !inuse) {
/* nonrecoverable error making socket */
return 0;

View File

@ -165,6 +165,10 @@ struct port_if {
/** length of addr field */
socklen_t addrlen;
/** prefix length of network address (in bits), for randomisation.
* if 0, no randomisation. */
int pfxlen;
/** the available ports array. These are unused.
* Only the first total-inuse part is filled. */
int* avail_ports;

View File

@ -161,6 +161,7 @@ warn_hosts(const char* typ, struct config_stub* list)
static void
interfacechecks(struct config_file* cfg)
{
int d;
struct sockaddr_storage a;
socklen_t alen;
int i, j;
@ -177,8 +178,8 @@ interfacechecks(struct config_file* cfg)
}
}
for(i=0; i<cfg->num_out_ifs; i++) {
if(!ipstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT,
&a, &alen)) {
if(!ipstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT, &a, &alen) &&
!netblockstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT, &a, &alen, &d)) {
fatal_exit("cannot parse outgoing-interface "
"specified as '%s'", cfg->out_ifs[i]);
}