summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJukka Rissanen <jukka.rissanen@linux.intel.com>2012-01-05 13:38:13 +0200
committerDaniel Wagner <daniel.wagner@bmw-carit.de>2012-01-05 13:17:26 +0100
commit5f5b3609c4f9884954623d7e6bf2ae1ae7096637 (patch)
treebd15c937959e83da53cd25a3fbbe6e923ee66cb7
parent590763aee614ce046c2927e716842a52c8da6a6e (diff)
downloadconnman-5f5b3609c4f9884954623d7e6bf2ae1ae7096637.tar.gz
connman-5f5b3609c4f9884954623d7e6bf2ae1ae7096637.tar.bz2
connman-5f5b3609c4f9884954623d7e6bf2ae1ae7096637.zip
dhcpv6: Handle address expiration by restarting the stack.
-rw-r--r--gdhcp/client.c18
-rw-r--r--gdhcp/gdhcp.h4
-rw-r--r--src/dhcpv6.c65
-rw-r--r--src/network.c28
4 files changed, 100 insertions, 15 deletions
diff --git a/gdhcp/client.c b/gdhcp/client.c
index cd428cff..7743aa50 100644
--- a/gdhcp/client.c
+++ b/gdhcp/client.c
@@ -138,6 +138,7 @@ struct _GDHCPClient {
struct in6_addr ia_ta;
time_t last_renew;
time_t last_rebind;
+ time_t expire;
};
static inline void debug(GDHCPClient *client, const char *format, ...)
@@ -627,7 +628,8 @@ void g_dhcpv6_client_create_iaid(GDHCPClient *dhcp_client, int index,
int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
uint32_t *T1, uint32_t *T2,
- time_t *last_renew, time_t *last_rebind)
+ time_t *last_renew, time_t *last_rebind,
+ time_t *expire)
{
if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
return -EINVAL;
@@ -644,6 +646,9 @@ int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
if (last_rebind != NULL)
*last_rebind = dhcp_client->last_rebind;
+ if (expire != NULL)
+ *expire = dhcp_client->expire;
+
return 0;
}
@@ -884,6 +889,7 @@ GDHCPClient *g_dhcp_client_new(GDHCPType type,
dhcp_client->duid = NULL;
dhcp_client->duid_len = 0;
dhcp_client->last_renew = dhcp_client->last_rebind = time(0);
+ dhcp_client->expire = 0;
*error = G_DHCP_CLIENT_ERROR_NONE;
@@ -1656,6 +1662,8 @@ static GList *get_addresses(GDHCPClient *dhcp_client,
else
memcpy(&dhcp_client->ia_ta, &addr,
sizeof(struct in6_addr));
+
+ g_dhcpv6_client_set_expire(dhcp_client, valid);
}
return list;
@@ -2490,6 +2498,14 @@ void g_dhcpv6_client_reset_rebind(GDHCPClient *dhcp_client)
dhcp_client->last_rebind = time(0);
}
+void g_dhcpv6_client_set_expire(GDHCPClient *dhcp_client, uint32_t timeout)
+{
+ if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+ return;
+
+ dhcp_client->expire = time(0) + timeout;
+}
+
uint16_t g_dhcpv6_client_get_status(GDHCPClient *dhcp_client)
{
if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
diff --git a/gdhcp/gdhcp.h b/gdhcp/gdhcp.h
index 29eea566..49b06b1a 100644
--- a/gdhcp/gdhcp.h
+++ b/gdhcp/gdhcp.h
@@ -144,12 +144,14 @@ void g_dhcpv6_client_create_iaid(GDHCPClient *dhcp_client, int index,
unsigned char *iaid);
int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
uint32_t *T1, uint32_t *T2,
- time_t *last_renew, time_t *last_rebind);
+ time_t *last_renew, time_t *last_rebind,
+ time_t *expire);
uint32_t g_dhcpv6_client_get_iaid(GDHCPClient *dhcp_client);
int g_dhcpv6_client_set_ia(GDHCPClient *dhcp_client, int index,
int code, uint32_t *T1, uint32_t *T2, gboolean add_iaaddr);
void g_dhcpv6_client_reset_renew(GDHCPClient *dhcp_client);
void g_dhcpv6_client_reset_rebind(GDHCPClient *dhcp_client);
+void g_dhcpv6_client_set_expire(GDHCPClient *dhcp_client, uint32_t timeout);
/* DHCP Server */
typedef enum {
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index fcd5d9cb..fd6e0ebe 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -579,10 +579,46 @@ static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp)
return g_dhcp_client_start(dhcp_client, NULL);
}
+static gboolean dhcpv6_restart(gpointer user_data)
+{
+ struct connman_dhcpv6 *dhcp = user_data;
+
+ if (dhcp->callback != NULL)
+ dhcp->callback(dhcp->network, FALSE);
+
+ return FALSE;
+}
+
+/*
+ * Check if we need to restart the solicitation procedure. This
+ * is done if all the addresses have expired. RFC 3315, 18.1.4
+ */
+static int check_restart(struct connman_dhcpv6 *dhcp)
+{
+ time_t current, expired;
+
+ g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
+ NULL, NULL, &expired);
+ current = time(0);
+
+ if (current > expired) {
+ DBG("expired by %d secs", (int)(current - expired));
+
+ g_timeout_add(0, dhcpv6_restart, dhcp);
+
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
static gboolean timeout_rebind(gpointer user_data)
{
struct connman_dhcpv6 *dhcp = user_data;
+ if (check_restart(dhcp) < 0)
+ return FALSE;
+
dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
DBG("rebind RT timeout %d msec", dhcp->RT);
@@ -636,7 +672,7 @@ static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
G_DHCPV6_SNTP_SERVERS);
- g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
+ g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL, NULL);
g_dhcpv6_client_set_ia(dhcp_client,
connman_network_get_index(dhcp->network),
dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
@@ -703,7 +739,7 @@ static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
G_DHCPV6_SNTP_SERVERS);
- g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
+ g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL, NULL);
g_dhcpv6_client_set_ia(dhcp_client,
connman_network_get_index(dhcp->network),
dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
@@ -723,6 +759,9 @@ static gboolean timeout_renew(gpointer user_data)
{
struct connman_dhcpv6 *dhcp = user_data;
+ if (check_restart(dhcp) < 0)
+ return FALSE;
+
dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
DBG("renew RT timeout %d msec", dhcp->RT);
@@ -754,9 +793,7 @@ int __connman_dhcpv6_start_renew(struct connman_network *network,
{
struct connman_dhcpv6 *dhcp;
uint32_t T1, T2;
- time_t last_renew, last_rebind, current;
-
- DBG("");
+ time_t last_renew, last_rebind, current, expired;
dhcp = g_hash_table_lookup(network_table, network);
if (dhcp == NULL)
@@ -768,8 +805,12 @@ int __connman_dhcpv6_start_renew(struct connman_network *network,
}
g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
- &last_renew, &last_rebind);
- DBG("T1 %u T2 %u", T1, T2);
+ &last_renew, &last_rebind, &expired);
+
+ current = time(0);
+
+ DBG("T1 %u T2 %u expires %lu current %lu", T1, T2,
+ (unsigned long)expired, current);
if (T1 == 0xffffffff)
/* RFC 3315, 22.4 */
@@ -781,15 +822,19 @@ int __connman_dhcpv6_start_renew(struct connman_network *network,
*/
T1 = 1800;
- current = time(0);
+ /* RFC 3315, 18.1.4, start solicit if expired */
+ if (current > expired) {
+ DBG("expired by %d secs", (int)(current - expired));
+ return -ETIMEDOUT;
+ }
dhcp->callback = callback;
if (T2 != 0xffffffff && T2 > 0 &&
(unsigned)current > (unsigned)last_rebind + T2) {
- /* RFC 3315, chapter 18.1.3, start rebind */
- int timeout = 0;
+ int timeout;
+ /* RFC 3315, chapter 18.1.3, start rebind */
if ((unsigned)current > (unsigned)last_renew + T1)
timeout = 0;
else
diff --git a/src/network.c b/src/network.c
index 1790fbae..bff0e7e2 100644
--- a/src/network.c
+++ b/src/network.c
@@ -1013,6 +1013,28 @@ err:
return err;
}
+static void autoconf_ipv6_set(struct connman_network *network);
+static void dhcpv6_callback(struct connman_network *network,
+ connman_bool_t success);
+
+/*
+ * Have a separate callback for renew so that we do not do autoconf
+ * in wrong phase as the dhcpv6_callback() is also called when doing
+ * DHCPv6 solicitation.
+ */
+static void dhcpv6_renew_callback(struct connman_network *network,
+ connman_bool_t success)
+{
+ if (success == TRUE)
+ dhcpv6_callback(network, success);
+ else {
+ stop_dhcpv6(network);
+
+ /* restart and do solicit again. */
+ autoconf_ipv6_set(network);
+ }
+}
+
static void dhcpv6_callback(struct connman_network *network,
connman_bool_t success)
{
@@ -1026,9 +1048,9 @@ static void dhcpv6_callback(struct connman_network *network,
return;
}
- __connman_dhcpv6_start_renew(network, dhcpv6_callback);
-
- return;
+ if (__connman_dhcpv6_start_renew(network,
+ dhcpv6_renew_callback) == -ETIMEDOUT)
+ dhcpv6_renew_callback(network, FALSE);
} else
stop_dhcpv6(network);
}