]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Fix #787: outgoing-interface netblock/64 ipv6 option to use linux
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 4 Jul 2016 14:51:30 +0000 (14:51 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 4 Jul 2016 14:51:30 +0000 (14:51 +0000)
  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

doc/Changelog
doc/unbound.conf.5.in
services/listen_dnsport.c
services/outside_network.c
services/outside_network.h
smallapp/unbound-checkconf.c

index 67cf334d2c7dbf6eb240ac47cc24b13dc97d133c..f7c1c717b1f5754c8eff8dcdda9b6386b6b2cd1a 100644 (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.
index f6219a32e170e1d66323d1cb7d3c485464866563..3393806255034fc1cda782c0a584636bbf9fcd23 100644 (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 
index 3083876eead4e6e87c59623d310becea6404c84a..63c0a5b3453f811a8797d133cc731dc14f7685b9 100644 (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;
index d9e34f46999de97e042dcb29a16900a8d9c55d71..b915c61457c2f78cf91fb62bf5613dbf9b66478d 100644 (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;
index 9a3270ee19a6de49a5859ffe62ea937c85ac741b..d6a448c0b68bb9f842c3439f081a452b2d45b389 100644 (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;
index 5b763c3a821d0b312892735fbcfbf19b68c1f2dc..b4536a173f0b83dcdf57df0867ea95565573d062 100644 (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]);
                }