Fix IPv6 DNS server adddress selection
authorHans Dedecker <dedeckeh@gmail.com>
Thu, 30 Oct 2014 11:30:32 +0000 (12:30 +0100)
committerHans Dedecker <dedeckeh@gmail.com>
Thu, 30 Oct 2014 13:05:41 +0000 (14:05 +0100)
Fix selection of IPv6 DNS address in DHCPv6 and RA overwrite as an address could be selected
with preferred lifetime zero while IPv6 addresses are in use with non zero preferred
lifetimes.
Fix tries to pick the IPv6 address with the longest preferred lifetime now.
Fixes also the issue an IPv6 address with preferred lifetime zero could be returned in DHCPv6
DNS server option while an IPv6 address with non zero preferred lifetime is returned as DNS
recursive RA option for the same set of available IPv6 addresses.

src/dhcpv6.c
src/odhcpd.c
src/odhcpd.h
src/router.c

index 30b4d25..7302d5d 100644 (file)
@@ -224,19 +224,19 @@ static void handle_client_request(void *addr, void *data, size_t len,
        } refresh = {htons(DHCPV6_OPT_INFO_REFRESH), htons(sizeof(uint32_t)),
                        htonl(600)};
 
-       struct odhcpd_ipaddr ipaddr;
-       struct in6_addr *dns_addr = iface->dns;
+       struct in6_addr dns_addr, *dns_addr_ptr = iface->dns;
        size_t dns_cnt = iface->dns_cnt;
 
-       if (dns_cnt == 0 && odhcpd_get_interface_addresses(iface->ifindex, &ipaddr, 1) == 1) {
-               dns_addr = &ipaddr.addr;
+       if ((dns_cnt == 0) &&
+               odhcpd_get_preferred_interface_address(iface->ifindex, &dns_addr)) {
+               dns_addr_ptr = &dns_addr;
                dns_cnt = 1;
        }
 
        struct {
                uint16_t type;
                uint16_t len;
-       } dns = {htons(DHCPV6_OPT_DNS_SERVERS), htons(dns_cnt * sizeof(*dns_addr))};
+       } dns = {htons(DHCPV6_OPT_DNS_SERVERS), htons(dns_cnt * sizeof(*dns_addr_ptr))};
 
 
 
@@ -274,7 +274,7 @@ static void handle_client_request(void *addr, void *data, size_t len,
                [IOV_DEST] = {&dest, (uint8_t*)&dest.clientid_type - (uint8_t*)&dest},
                [IOV_MAXRT] = {&maxrt, sizeof(maxrt)},
                [IOV_DNS] = {&dns, (dns_cnt) ? sizeof(dns) : 0},
-               [IOV_DNS_ADDR] = {dns_addr, dns_cnt * sizeof(*dns_addr)},
+               [IOV_DNS_ADDR] = {dns_addr_ptr, dns_cnt * sizeof(*dns_addr_ptr)},
                [IOV_SEARCH] = {&search, (search_len) ? sizeof(search) : 0},
                [IOV_SEARCH_DOMAIN] = {search_domain, search_len},
                [IOV_PDBUF] = {pdbuf, 0},
@@ -333,7 +333,7 @@ static void handle_client_request(void *addr, void *data, size_t len,
                        if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)) {
                                struct odhcpd_ipaddr addrs[32];
                                ssize_t len = odhcpd_get_interface_addresses(0, addrs,
-                                               sizeof(addrs) / sizeof(*addrs));
+                                               ARRAY_SIZE(addrs));
 
                                for (ssize_t i = 0; i < len; ++i)
                                        if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)
@@ -455,15 +455,15 @@ static void relay_server_response(uint8_t *data, size_t len)
                if (is_authenticated)
                        return; // Impossible to rewrite
 
-               struct odhcpd_ipaddr ip;
                const struct in6_addr *rewrite = iface->dns;
+               struct in6_addr addr;
                size_t rewrite_cnt = iface->dns_cnt;
 
                if (rewrite_cnt == 0) {
-                       if (odhcpd_get_interface_addresses(iface->ifindex, &ip, 1) < 1)
+                       if (odhcpd_get_preferred_interface_address(iface->ifindex, &addr) < 1)
                                return; // Unable to get interface address
 
-                       rewrite = &ip.addr;
+                       rewrite = &addr;
                        rewrite_cnt = 1;
                }
 
index 0edf63e..3c384e8 100644 (file)
@@ -263,6 +263,25 @@ ssize_t odhcpd_get_interface_addresses(int ifindex,
        return ret;
 }
 
+int odhcpd_get_preferred_interface_address(int ifindex, struct in6_addr *addr)
+{
+       struct odhcpd_ipaddr ipaddrs[8];
+       ssize_t ip_cnt = odhcpd_get_interface_addresses(ifindex, ipaddrs, ARRAY_SIZE(ipaddrs));
+       uint32_t preferred = 0;
+       int ret = 0;
+
+       for (ssize_t i = 0; i < ip_cnt; i++) {
+               struct odhcpd_ipaddr *ipaddr = &ipaddrs[i];
+
+               if (ipaddr->preferred > preferred || !preferred) {
+                       preferred = ipaddr->preferred;
+                       *addr = ipaddr->addr;
+                       ret = 1;
+               }
+       }
+
+       return ret;
+}
 
 struct interface* odhcpd_get_interface_by_index(int ifindex)
 {
index c0e509d..bc837d5 100644 (file)
@@ -186,6 +186,7 @@ ssize_t odhcpd_send(int socket, struct sockaddr_in6 *dest,
                const struct interface *iface);
 ssize_t odhcpd_get_interface_addresses(int ifindex,
                struct odhcpd_ipaddr *addrs, size_t cnt);
+int odhcpd_get_preferred_interface_address(int ifindex, struct in6_addr *addr);
 struct interface* odhcpd_get_interface_by_name(const char *name);
 int odhcpd_get_interface_mtu(const char *ifname);
 int odhcpd_get_mac(const struct interface *iface, uint8_t mac[6]);
index 0b15dac..3d26c58 100644 (file)
@@ -537,7 +537,6 @@ static void forward_router_advertisement(uint8_t *data, size_t len)
        struct sockaddr_in6 all_nodes = {AF_INET6, 0, 0, ALL_IPV6_NODES, 0};
        struct iovec iov = {data, len};
 
-       struct odhcpd_ipaddr addr;
        struct interface *iface;
        list_for_each_entry(iface, &interfaces, head) {
                if (iface->ra != RELAYD_RELAY || iface->master)
@@ -550,13 +549,14 @@ static void forward_router_advertisement(uint8_t *data, size_t len)
                // If we have to rewrite DNS entries
                if (iface->always_rewrite_dns && dns_ptr && dns_count > 0) {
                        const struct in6_addr *rewrite = iface->dns;
+                       struct in6_addr addr;
                        size_t rewrite_cnt = iface->dns_cnt;
 
                        if (rewrite_cnt == 0) {
-                               if (odhcpd_get_interface_addresses(iface->ifindex, &addr, 1) < 1)
+                               if (odhcpd_get_preferred_interface_address(iface->ifindex, &addr) < 1)
                                        continue; // Unable to comply
 
-                               rewrite = &addr.addr;
+                               rewrite = &addr;
                                rewrite_cnt = 1;
                        }