From 8aa986762c7774844e6199b8fa2514d639df2500 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 5 Jan 2012 13:38:11 +0200 Subject: [PATCH] dhcpv6: Renew message implemented. --- gdhcp/client.c | 46 ++++++++++++++++++++- gdhcp/gdhcp.h | 5 ++- src/connman.h | 2 + src/dhcpv6.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/network.c | 2 + 5 files changed, 177 insertions(+), 3 deletions(-) diff --git a/gdhcp/client.c b/gdhcp/client.c index a8fedb1..a1f0309 100644 --- a/gdhcp/client.c +++ b/gdhcp/client.c @@ -72,6 +72,7 @@ typedef enum _dhcp_client_state { INFORMATION_REQ, SOLICITATION, REQUEST, + RENEW, } ClientState; struct _GDHCPClient { @@ -120,6 +121,8 @@ struct _GDHCPClient { gpointer advertise_data; GDHCPClientEventFunc request_cb; gpointer request_data; + GDHCPClientEventFunc renew_cb; + gpointer renew_data; char *last_address; unsigned char *duid; int duid_len; @@ -130,6 +133,7 @@ struct _GDHCPClient { uint32_t T1, T2; struct in6_addr ia_na; struct in6_addr ia_ta; + time_t last_renew; }; static inline void debug(GDHCPClient *client, const char *format, ...) @@ -618,7 +622,7 @@ 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) + uint32_t *T1, uint32_t *T2, time_t *last_renew) { if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4) return -EINVAL; @@ -629,6 +633,9 @@ int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client, if (T2 != NULL) *T2 = dhcp_client->T2; + if (last_renew != NULL) + *last_renew = dhcp_client->last_renew; + return 0; } @@ -787,6 +794,11 @@ static int send_dhcpv6_request(GDHCPClient *dhcp_client) return send_dhcpv6_msg(dhcp_client, DHCPV6_REQUEST, "request"); } +static int send_dhcpv6_renew(GDHCPClient *dhcp_client) +{ + return send_dhcpv6_msg(dhcp_client, DHCPV6_RENEW, "renew"); +} + static int send_information_req(GDHCPClient *dhcp_client) { return send_dhcpv6_msg(dhcp_client, DHCPV6_INFORMATION_REQ, @@ -858,6 +870,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); *error = G_DHCP_CLIENT_ERROR_NONE; @@ -1966,6 +1979,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, break; case INFORMATION_REQ: case REQUEST: + case RENEW: if (dhcp_client->type != G_DHCP_IPV6) return TRUE; @@ -2007,6 +2021,11 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, dhcp_client->request_data); return TRUE; } + if (dhcp_client->renew_cb != NULL) { + dhcp_client->renew_cb(dhcp_client, + dhcp_client->renew_data); + return TRUE; + } break; default: break; @@ -2130,6 +2149,16 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address) return re; } send_dhcpv6_request(dhcp_client); + + } else if (dhcp_client->renew_cb) { + dhcp_client->state = RENEW; + 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_renew(dhcp_client); } return 0; @@ -2268,6 +2297,12 @@ void g_dhcp_client_register_event(GDHCPClient *dhcp_client, dhcp_client->request_cb = func; dhcp_client->request_data = data; return; + case G_DHCP_CLIENT_EVENT_RENEW: + if (dhcp_client->type == G_DHCP_IPV4) + return; + dhcp_client->renew_cb = func; + dhcp_client->renew_data = data; + return; } } @@ -2306,6 +2341,7 @@ char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client) case INFORMATION_REQ: case SOLICITATION: case REQUEST: + case RENEW: break; } return NULL; @@ -2402,6 +2438,14 @@ void g_dhcpv6_client_set_send(GDHCPClient *dhcp_client, } } +void g_dhcpv6_client_reset_renew(GDHCPClient *dhcp_client) +{ + if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4) + return; + + dhcp_client->last_renew = 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 22a28a5..3982e3e 100644 --- a/gdhcp/gdhcp.h +++ b/gdhcp/gdhcp.h @@ -56,6 +56,7 @@ typedef enum { G_DHCP_CLIENT_EVENT_SOLICITATION, G_DHCP_CLIENT_EVENT_ADVERTISE, G_DHCP_CLIENT_EVENT_REQUEST, + G_DHCP_CLIENT_EVENT_RENEW, } GDHCPClientEvent; typedef enum { @@ -141,10 +142,12 @@ int g_dhcpv6_client_set_oro(GDHCPClient *dhcp_client, int args, ...); 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); + uint32_t *T1, uint32_t *T2, + time_t *last_renew); 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); /* DHCP Server */ typedef enum { diff --git a/src/connman.h b/src/connman.h index f48f922..d99d4b4 100644 --- a/src/connman.h +++ b/src/connman.h @@ -284,6 +284,8 @@ int __connman_dhcpv6_start_info(struct connman_network *network, void __connman_dhcpv6_stop(struct connman_network *network); int __connman_dhcpv6_start(struct connman_network *network, GSList *prefixes, dhcp_cb callback); +int __connman_dhcpv6_start_renew(struct connman_network *network, + dhcp_cb callback); int __connman_ipv4_init(void); void __connman_ipv4_cleanup(void); diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 34395fe..b5b9172 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -46,6 +46,8 @@ #define REQ_TIMEOUT (1 * 1000) #define REQ_MAX_RT (30 * 1000) #define REQ_MAX_RC 10 +#define REN_TIMEOUT (10 * 1000) +#define REN_MAX_RT (600 * 1000) struct connman_dhcpv6 { @@ -224,6 +226,10 @@ static void clear_callbacks(GDHCPClient *dhcp_client) NULL, NULL); g_dhcp_client_register_event(dhcp_client, + G_DHCP_CLIENT_EVENT_RENEW, + NULL, NULL); + + g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_INFORMATION_REQ, NULL, NULL); } @@ -552,7 +558,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); + g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, 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, @@ -591,6 +597,123 @@ static gboolean timeout_request(gpointer user_data) return FALSE; } +static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data) +{ + DBG(""); + + g_dhcpv6_client_reset_renew(dhcp_client); + + re_cb(dhcp_client, user_data); +} + +static int dhcpv6_renew(struct connman_dhcpv6 *dhcp) +{ + GDHCPClient *dhcp_client; + uint32_t T1, T2; + + 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_SERVERID); + 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_get_timeouts(dhcp_client, &T1, &T2, 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, + &T1, &T2, TRUE); + + clear_callbacks(dhcp_client); + + g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW, + renew_cb, dhcp); + + dhcp->dhcp_client = dhcp_client; + + return g_dhcp_client_start(dhcp_client, NULL); +} + +static gboolean timeout_renew(gpointer user_data) +{ + struct connman_dhcpv6 *dhcp = user_data; + + dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT); + + DBG("renew RT timeout %d msec", dhcp->RT); + + dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp); + + g_dhcp_client_start(dhcp->dhcp_client, NULL); + + return FALSE; +} + +static gboolean start_renew(gpointer user_data) +{ + struct connman_dhcpv6 *dhcp = user_data; + + dhcp->RT = REN_TIMEOUT * (1 + get_random()); + + DBG("renew initial RT timeout %d msec", dhcp->RT); + + dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp); + + dhcpv6_renew(dhcp); + + return FALSE; +} + +int __connman_dhcpv6_start_renew(struct connman_network *network, + dhcp_cb callback) +{ + struct connman_dhcpv6 *dhcp; + uint32_t T1, T2; + time_t last_renew, current; + + DBG(""); + + dhcp = g_hash_table_lookup(network_table, network); + if (dhcp == NULL) + return -ENOENT; + + if (dhcp->timeout > 0) { + g_source_remove(dhcp->timeout); + dhcp->timeout = 0; + } + + g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2, + &last_renew); + DBG("T1 %u T2 %u", T1, T2); + + if (T1 == 0xffffffff) + /* RFC 3315, 22.4 */ + return 0; + + if (T1 == 0) + /* RFC 3315, 22.4 + * Client can choose the timeout. + */ + T1 = 1800; + + current = time(0); + + dhcp->callback = callback; + + DBG("renew after %d secs", T1); + + dhcp->timeout = g_timeout_add_seconds(T1, start_renew, dhcp); + + return 0; +} + static int dhcpv6_release(struct connman_dhcpv6 *dhcp) { DBG("dhcp %p", dhcp); diff --git a/src/network.c b/src/network.c index f9aed49..1790fba 100644 --- a/src/network.c +++ b/src/network.c @@ -1026,6 +1026,8 @@ static void dhcpv6_callback(struct connman_network *network, return; } + __connman_dhcpv6_start_renew(network, dhcpv6_callback); + return; } else stop_dhcpv6(network); -- 2.7.4