From: Jukka Rissanen Date: Thu, 5 Jan 2012 11:38:13 +0000 (+0200) Subject: dhcpv6: Handle address expiration by restarting the stack. X-Git-Tag: 2.0_alpha~749 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5f5b3609c4f9884954623d7e6bf2ae1ae7096637;p=framework%2Fconnectivity%2Fconnman.git dhcpv6: Handle address expiration by restarting the stack. --- diff --git a/gdhcp/client.c b/gdhcp/client.c index cd428cf..7743aa5 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 29eea56..49b06b1 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 fcd5d9c..fd6e0eb 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 1790fba..bff0e7e 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); }