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
.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
#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,
}
#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;
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;
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);
/* 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;