resolver: Send RS before RDNSS lifetime expires
authorElena Tebesoi <elena.tebesoi@gmail.com>
Mon, 21 May 2012 12:49:42 +0000 (14:49 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Tue, 22 May 2012 19:13:32 +0000 (21:13 +0200)
Implemented feature from RFC 6106 section
'5.1. Recursive DNS Server Option':
"Lifetime      32-bit unsigned integer.
...
Hosts MAY send a Router Solicitation to ensure
the RDNSS information is fresh before the interval expires."

Host will send RS when a certain threshold of RDNSS lifetime is reached.
Values which can be adjusted:
- lifetime threshold - set to 80% from lifetime
- number of retries in case RA is not received - set to 0
- time between retries, in case RA is not received - set to 3 seconds

src/connman.h
src/network.c
src/resolver.c

index 734213d..7f998eb 100644 (file)
@@ -147,6 +147,9 @@ typedef void (*__connman_inet_rs_cb_t) (struct nd_router_advert *reply,
 
 int __connman_inet_ipv6_send_rs(int index, int timeout,
                        __connman_inet_rs_cb_t callback, void *user_data);
+
+int __connman_refresh_rs_ipv6(struct connman_network *network, int index);
+
 GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr,
                                        unsigned int length);
 
index 10f1bf3..6fe1d9c 100644 (file)
 
 #include "connman.h"
 
+/*
+ * How many times to send RS with the purpose of
+ * refreshing RDNSS entries before they actually expire.
+ * With a value of 1, one RS will be sent, with no retries.
+ */
+#define RS_REFRESH_COUNT       1
+
+/*
+ * Value in seconds to wait for RA after RS was sent.
+ * After this time elapsed, we can send another RS.
+ */
+#define RS_REFRESH_TIMEOUT     3
+
 static GSList *network_list = NULL;
 static GSList *driver_list = NULL;
 
@@ -46,6 +59,7 @@ struct connman_network {
        char *path;
        int index;
        int router_solicit_count;
+       int router_solicit_refresh_count;
 
        struct connman_network_driver *driver;
        void *driver_data;
@@ -1113,6 +1127,58 @@ static void check_dhcpv6(struct nd_router_advert *reply,
        connman_network_unref(network);
 }
 
+static void receive_refresh_rs_reply(struct nd_router_advert *reply,
+               unsigned int length, void *user_data)
+{
+       struct connman_network *network = user_data;
+
+       DBG("reply %p", reply);
+
+       if (reply == NULL) {
+               /*
+                * Router solicitation message seem to get lost easily so
+                * try to send it again.
+                */
+               if (network->router_solicit_refresh_count > 1) {
+                       network->router_solicit_refresh_count--;
+                       DBG("re-send router solicitation %d",
+                                       network->router_solicit_refresh_count);
+                       __connman_inet_ipv6_send_rs(network->index,
+                                       RS_REFRESH_TIMEOUT,
+                                       receive_refresh_rs_reply,
+                                       network);
+                       return;
+               }
+       }
+
+       /* RS refresh not in progress anymore */
+       network->router_solicit_refresh_count = 0;
+
+       connman_network_unref(network);
+       return;
+}
+
+int __connman_refresh_rs_ipv6(struct connman_network *network, int index)
+{
+       int ret = 0;
+
+       DBG("network %p index %d", network, index);
+
+       /* Send only one RS for all RDNSS entries which are about to expire */
+       if (network->router_solicit_refresh_count > 0) {
+               DBG("RS refresh already started");
+               return 0;
+       }
+
+       network->router_solicit_refresh_count = RS_REFRESH_COUNT;
+
+       connman_network_ref(network);
+
+       ret = __connman_inet_ipv6_send_rs(index, RS_REFRESH_TIMEOUT,
+                       receive_refresh_rs_reply, network);
+       return ret;
+}
+
 static void autoconf_ipv6_set(struct connman_network *network)
 {
        struct connman_service *service;
index 421b12f..4780ac1 100644 (file)
 
 #define RESOLVER_FLAG_PUBLIC (1 << 0)
 
+/*
+ * Threshold for RDNSS lifetime. Will be used to trigger RS
+ * before RDNSS entries actually expire
+ */
+#define RESOLVER_LIFETIME_REFRESH_THRESHOLD 0.8
+
 struct entry_data {
        char *interface;
        char *domain;
        char *server;
        unsigned int flags;
+       unsigned int lifetime;
        guint timeout;
 };
 
@@ -253,11 +260,47 @@ static gboolean resolver_expire_cb(gpointer user_data)
        return FALSE;
 }
 
+static gboolean resolver_refresh_cb(gpointer user_data)
+{
+       struct entry_data *entry = user_data;
+       int index;
+       unsigned int interval;
+       struct connman_service *service = NULL;
+
+       /* Round up what we have left from lifetime */
+       interval = entry->lifetime *
+               (1 - RESOLVER_LIFETIME_REFRESH_THRESHOLD) + 1.0;
+
+       DBG("RDNSS start interface %s domain %s "
+                       "server %s remaining lifetime %d",
+                       entry->interface, entry->domain,
+                       entry->server, interval);
+
+       entry->timeout = g_timeout_add_seconds(interval,
+                       resolver_expire_cb, entry);
+
+       index = connman_inet_ifindex(entry->interface);
+       if (index >= 0) {
+               service = __connman_service_lookup_from_index(index);
+               if (service != NULL) {
+                       /*
+                        * Send Router Solicitation to refresh RDNSS entries
+                        * before their lifetime expires
+                        */
+                       __connman_refresh_rs_ipv6(
+                                       __connman_service_get_network(service),
+                                       index);
+               }
+       }
+       return FALSE;
+}
+
 static int append_resolver(const char *interface, const char *domain,
                                const char *server, unsigned int lifetime,
                                                        unsigned int flags)
 {
        struct entry_data *entry;
+       unsigned int interval;
 
        DBG("interface %s domain %s server %s lifetime %d flags %d",
                                interface, domain, server, lifetime, flags);
@@ -273,10 +316,17 @@ static int append_resolver(const char *interface, const char *domain,
        entry->domain = g_strdup(domain);
        entry->server = g_strdup(server);
        entry->flags = flags;
+       entry->lifetime = lifetime;
        if (lifetime) {
                int index;
-               entry->timeout = g_timeout_add_seconds(lifetime,
-                                               resolver_expire_cb, entry);
+               interval = lifetime * RESOLVER_LIFETIME_REFRESH_THRESHOLD;
+
+               DBG("RDNSS start interface %s domain %s "
+                               "server %s lifetime threshold %d",
+                               interface, domain, server, interval);
+
+               entry->timeout = g_timeout_add_seconds(interval,
+                               resolver_refresh_cb, entry);
 
                /*
                 * We update the service only for those nameservers
@@ -350,6 +400,7 @@ int connman_resolver_append_lifetime(const char *interface, const char *domain,
                                const char *server, unsigned int lifetime)
 {
        GSList *list;
+       unsigned int interval;
 
        DBG("interface %s domain %s server %s lifetime %d",
                                interface, domain, server, lifetime);
@@ -373,8 +424,14 @@ int connman_resolver_append_lifetime(const char *interface, const char *domain,
                        return 0;
                }
 
-               entry->timeout = g_timeout_add_seconds(lifetime,
-                                               resolver_expire_cb, entry);
+               interval = lifetime * RESOLVER_LIFETIME_REFRESH_THRESHOLD;
+
+               DBG("RDNSS start interface %s domain %s "
+                               "server %s lifetime threshold %d",
+                               interface, domain, server, interval);
+
+               entry->timeout = g_timeout_add_seconds(interval,
+                               resolver_refresh_cb, entry);
                return 0;
        }