rework IPv6 dns address selection (FS#635)
authorHans Dedecker <dedeckeh@gmail.com>
Wed, 29 Mar 2017 15:03:29 +0000 (17:03 +0200)
committerHans Dedecker <dedeckeh@gmail.com>
Thu, 30 Mar 2017 12:48:02 +0000 (14:48 +0200)
Don't return anymore the link local IPv6 address as DNS IPv6 address
since different OS implementations (e.g. android, ...) cannot handle
a link local IPv6 address as DNS address.

IPv6 DNS address selection is reworked as follows :
-Consider all global/ULA IPv6 address having a valid lifetime
-Give preference to global/ULA IPv6 addresses being not deprecated
-Give preference to ULA IPv6 addresses over IPv6 global addresses
-Give preference to the IPv6 address with the longest preferred lifetime in
its selected category (ULA or global)
-If no global/ULA IPv6 address is present use the IPv6 link local address

Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
src/dhcpv6.c
src/odhcpd.c
src/odhcpd.h
src/router.c

index 4cd9e0293e0cb1390ebb6647c218cc51d02ebf8c..ebb0c805b9ab2fcfaaa4c1396afe23138b689f18 100644 (file)
@@ -232,7 +232,7 @@ static void handle_client_request(void *addr, void *data, size_t len,
        size_t dns_cnt = iface->dns_cnt;
 
        if ((dns_cnt == 0) &&
-               !odhcpd_get_linklocal_interface_address(iface->ifindex, &dns_addr)) {
+               !odhcpd_get_interface_dns_addr(iface, &dns_addr)) {
                dns_addr_ptr = &dns_addr;
                dns_cnt = 1;
        }
@@ -464,7 +464,7 @@ static void relay_server_response(uint8_t *data, size_t len)
                size_t rewrite_cnt = iface->dns_cnt;
 
                if (rewrite_cnt == 0) {
-                       if (odhcpd_get_linklocal_interface_address(iface->ifindex, &addr))
+                       if (odhcpd_get_interface_dns_addr(iface, &addr))
                                return; // Unable to get interface address
 
                        rewrite = &addr;
index 8a18fbf4d23446236e7bbf3440486a3f79917c02..8a1c66e6721a37c83af600ba8b29ef401744e29b 100644 (file)
@@ -336,7 +336,7 @@ out:
        return ctxt.ret;
 }
 
-int odhcpd_get_linklocal_interface_address(int ifindex, struct in6_addr *lladdr)
+static int odhcpd_get_linklocal_interface_address(int ifindex, struct in6_addr *lladdr)
 {
        int status = -1;
        struct sockaddr_in6 addr = {AF_INET6, 0, 0, ALL_IPV6_ROUTERS, ifindex};
@@ -354,6 +354,51 @@ int odhcpd_get_linklocal_interface_address(int ifindex, struct in6_addr *lladdr)
        return status;
 }
 
+/*
+ * DNS address selection criteria order :
+ * - use IPv6 address with valid lifetime if none is yet selected
+ * - use IPv6 address with a preferred lifetime if the already selected IPv6 address is deprecated
+ * - use an IPv6 ULA address if the already selected IPv6 address is not an ULA address
+ * - use the IPv6 address with the longest preferred lifetime
+ */
+int odhcpd_get_interface_dns_addr(const struct interface *iface, struct in6_addr *addr)
+{
+       time_t now = odhcpd_time();
+       ssize_t m = -1;
+
+       for (size_t i = 0; i < iface->ia_addr_len; ++i) {
+               if (iface->ia_addr[i].valid <= (uint32_t)now)
+                       continue;
+
+               if (m < 0) {
+                       m = i;
+                       continue;
+               }
+
+               if (iface->ia_addr[m].preferred >= (uint32_t)now &&
+                               iface->ia_addr[i].preferred < (uint32_t)now)
+                       continue;
+
+               if (IN6_IS_ADDR_ULA(&iface->ia_addr[i].addr)) {
+                       if (!IN6_IS_ADDR_ULA(&iface->ia_addr[m].addr)) {
+                               m = i;
+                               continue;
+                       }
+               } else if (IN6_IS_ADDR_ULA(&iface->ia_addr[m].addr))
+                       continue;
+
+               if (iface->ia_addr[i].preferred > iface->ia_addr[m].preferred)
+                       m = i;
+       }
+
+       if (m >= 0) {
+               *addr = iface->ia_addr[m].addr;
+               return 0;
+       }
+
+       return odhcpd_get_linklocal_interface_address(iface->ifindex, addr);
+}
+
 int odhcpd_setup_route(const struct in6_addr *addr, const int prefixlen,
                const struct interface *iface, const struct in6_addr *gw,
                const uint32_t metric, const bool add)
index 973309090f6ca01aafaee39a93a2c4669c9e82ee..4ddadbee635ae1c7d21c6dfed424b5f5690f05a7 100644 (file)
@@ -203,7 +203,8 @@ 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_linklocal_interface_address(int ifindex, struct in6_addr *lladdr);
+int odhcpd_get_interface_dns_addr(const struct interface *iface,
+               struct in6_addr *addr);
 struct interface* odhcpd_get_interface_by_name(const char *name);
 int odhcpd_get_interface_config(const char *ifname, const char *what);
 int odhcpd_get_mac(const struct interface *iface, uint8_t mac[6]);
index 6e5111a0b5f5cb94a77af94e086c5d01b03cdbdc..feae5c7e1b28516f28e483645ffffdfade974418 100644 (file)
@@ -319,7 +319,7 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add
        struct in6_addr dns_pref, *dns_addr = &dns_pref;
        size_t dns_cnt = 1;
 
-       odhcpd_get_linklocal_interface_address(iface->ifindex, &dns_pref);
+       odhcpd_get_interface_dns_addr(iface, &dns_pref);
 
        for (ssize_t i = 0; i < ipcnt; ++i) {
                struct odhcpd_ipaddr *addr = &addrs[i];
@@ -617,7 +617,7 @@ static void forward_router_advertisement(uint8_t *data, size_t len)
                        size_t rewrite_cnt = iface->dns_cnt;
 
                        if (rewrite_cnt == 0) {
-                               if (odhcpd_get_linklocal_interface_address(iface->ifindex, &addr))
+                               if (odhcpd_get_interface_dns_addr(iface, &addr))
                                        continue; // Unable to comply
 
                                rewrite = &addr;