diff options
-rw-r--r-- | gdhcp/client.c | 48 | ||||
-rw-r--r-- | gdhcp/gdhcp.h | 4 | ||||
-rw-r--r-- | src/dhcpv6.c | 107 |
3 files changed, 150 insertions, 9 deletions
diff --git a/gdhcp/client.c b/gdhcp/client.c index a1f03097..cd428cff 100644 --- a/gdhcp/client.c +++ b/gdhcp/client.c @@ -73,6 +73,7 @@ typedef enum _dhcp_client_state { SOLICITATION, REQUEST, RENEW, + REBIND, } ClientState; struct _GDHCPClient { @@ -123,6 +124,8 @@ struct _GDHCPClient { gpointer request_data; GDHCPClientEventFunc renew_cb; gpointer renew_data; + GDHCPClientEventFunc rebind_cb; + gpointer rebind_data; char *last_address; unsigned char *duid; int duid_len; @@ -134,6 +137,7 @@ struct _GDHCPClient { struct in6_addr ia_na; struct in6_addr ia_ta; time_t last_renew; + time_t last_rebind; }; static inline void debug(GDHCPClient *client, const char *format, ...) @@ -622,7 +626,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) + uint32_t *T1, uint32_t *T2, + time_t *last_renew, time_t *last_rebind) { if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4) return -EINVAL; @@ -636,6 +641,9 @@ int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client, if (last_renew != NULL) *last_renew = dhcp_client->last_renew; + if (last_rebind != NULL) + *last_rebind = dhcp_client->last_rebind; + return 0; } @@ -799,6 +807,11 @@ static int send_dhcpv6_renew(GDHCPClient *dhcp_client) return send_dhcpv6_msg(dhcp_client, DHCPV6_RENEW, "renew"); } +static int send_dhcpv6_rebind(GDHCPClient *dhcp_client) +{ + return send_dhcpv6_msg(dhcp_client, DHCPV6_REBIND, "rebind"); +} + static int send_information_req(GDHCPClient *dhcp_client) { return send_dhcpv6_msg(dhcp_client, DHCPV6_INFORMATION_REQ, @@ -870,7 +883,7 @@ GDHCPClient *g_dhcp_client_new(GDHCPType type, dhcp_client->require_list = NULL; dhcp_client->duid = NULL; dhcp_client->duid_len = 0; - dhcp_client->last_renew = time(0); + dhcp_client->last_renew = dhcp_client->last_rebind = time(0); *error = G_DHCP_CLIENT_ERROR_NONE; @@ -1980,6 +1993,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, case INFORMATION_REQ: case REQUEST: case RENEW: + case REBIND: if (dhcp_client->type != G_DHCP_IPV6) return TRUE; @@ -2026,6 +2040,11 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, dhcp_client->renew_data); return TRUE; } + if (dhcp_client->rebind_cb != NULL) { + dhcp_client->rebind_cb(dhcp_client, + dhcp_client->rebind_data); + return TRUE; + } break; default: break; @@ -2159,6 +2178,16 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address) return re; } send_dhcpv6_renew(dhcp_client); + + } else if (dhcp_client->rebind_cb) { + dhcp_client->state = REBIND; + re = switch_listening_mode(dhcp_client, L3); + if (re != 0) { + switch_listening_mode(dhcp_client, L_NONE); + dhcp_client->state = 0; + return re; + } + send_dhcpv6_rebind(dhcp_client); } return 0; @@ -2303,6 +2332,12 @@ void g_dhcp_client_register_event(GDHCPClient *dhcp_client, dhcp_client->renew_cb = func; dhcp_client->renew_data = data; return; + case G_DHCP_CLIENT_EVENT_REBIND: + if (dhcp_client->type == G_DHCP_IPV4) + return; + dhcp_client->rebind_cb = func; + dhcp_client->rebind_data = data; + return; } } @@ -2342,6 +2377,7 @@ char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client) case SOLICITATION: case REQUEST: case RENEW: + case REBIND: break; } return NULL; @@ -2446,6 +2482,14 @@ void g_dhcpv6_client_reset_renew(GDHCPClient *dhcp_client) dhcp_client->last_renew = time(0); } +void g_dhcpv6_client_reset_rebind(GDHCPClient *dhcp_client) +{ + if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4) + return; + + dhcp_client->last_rebind = time(0); +} + 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 3982e3ef..29eea566 100644 --- a/gdhcp/gdhcp.h +++ b/gdhcp/gdhcp.h @@ -57,6 +57,7 @@ typedef enum { G_DHCP_CLIENT_EVENT_ADVERTISE, G_DHCP_CLIENT_EVENT_REQUEST, G_DHCP_CLIENT_EVENT_RENEW, + G_DHCP_CLIENT_EVENT_REBIND, } GDHCPClientEvent; typedef enum { @@ -143,11 +144,12 @@ 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_renew, time_t *last_rebind); 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); /* DHCP Server */ typedef enum { diff --git a/src/dhcpv6.c b/src/dhcpv6.c index b5b91727..fcd5d9cb 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -48,6 +48,8 @@ #define REQ_MAX_RC 10 #define REN_TIMEOUT (10 * 1000) #define REN_MAX_RT (600 * 1000) +#define REB_TIMEOUT (10 * 1000) +#define REB_MAX_RT (600 * 1000) struct connman_dhcpv6 { @@ -230,6 +232,10 @@ static void clear_callbacks(GDHCPClient *dhcp_client) NULL, NULL); g_dhcp_client_register_event(dhcp_client, + G_DHCP_CLIENT_EVENT_REBIND, + NULL, NULL); + + g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_INFORMATION_REQ, NULL, NULL); } @@ -531,6 +537,78 @@ static void re_cb(GDHCPClient *dhcp_client, gpointer user_data) } } +static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data) +{ + DBG(""); + + g_dhcpv6_client_reset_rebind(dhcp_client); + g_dhcpv6_client_reset_renew(dhcp_client); + + re_cb(dhcp_client, user_data); +} + +static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp) +{ + GDHCPClient *dhcp_client; + + DBG("dhcp %p", dhcp); + + dhcp_client = dhcp->dhcp_client; + + g_dhcp_client_clear_requests(dhcp_client); + + g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID); + g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS); + g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS); + + g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS, + G_DHCPV6_SNTP_SERVERS); + + 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, + NULL, NULL, FALSE); + + clear_callbacks(dhcp_client); + + g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND, + rebind_cb, dhcp); + + dhcp->dhcp_client = dhcp_client; + + return g_dhcp_client_start(dhcp_client, NULL); +} + +static gboolean timeout_rebind(gpointer user_data) +{ + struct connman_dhcpv6 *dhcp = user_data; + + dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT); + + DBG("rebind RT timeout %d msec", dhcp->RT); + + dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp); + + g_dhcp_client_start(dhcp->dhcp_client, NULL); + + return FALSE; +} + +static gboolean start_rebind(gpointer user_data) +{ + struct connman_dhcpv6 *dhcp = user_data; + + dhcp->RT = REB_TIMEOUT * (1 + get_random()); + + DBG("rebind initial RT timeout %d msec", dhcp->RT); + + dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp); + + dhcpv6_rebind(dhcp); + + return FALSE; +} + static void request_cb(GDHCPClient *dhcp_client, gpointer user_data) { DBG(""); @@ -558,7 +636,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); + g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, 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, @@ -625,7 +703,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); + g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, 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, @@ -676,7 +754,7 @@ int __connman_dhcpv6_start_renew(struct connman_network *network, { struct connman_dhcpv6 *dhcp; uint32_t T1, T2; - time_t last_renew, current; + time_t last_renew, last_rebind, current; DBG(""); @@ -690,7 +768,7 @@ int __connman_dhcpv6_start_renew(struct connman_network *network, } g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2, - &last_renew); + &last_renew, &last_rebind); DBG("T1 %u T2 %u", T1, T2); if (T1 == 0xffffffff) @@ -707,10 +785,27 @@ int __connman_dhcpv6_start_renew(struct connman_network *network, dhcp->callback = callback; - DBG("renew after %d secs", T1); + if (T2 != 0xffffffff && T2 > 0 && + (unsigned)current > (unsigned)last_rebind + T2) { + /* RFC 3315, chapter 18.1.3, start rebind */ + int timeout = 0; - dhcp->timeout = g_timeout_add_seconds(T1, start_renew, dhcp); + if ((unsigned)current > (unsigned)last_renew + T1) + timeout = 0; + else + timeout = last_renew - current + T1; + /* + * If we just did a renew, do not restart the rebind + * immediately. + */ + dhcp->timeout = g_timeout_add_seconds(timeout, start_rebind, + dhcp); + } else { + DBG("renew after %d secs", T1); + + dhcp->timeout = g_timeout_add_seconds(T1, start_renew, dhcp); + } return 0; } |