From: Jukka Rissanen Date: Thu, 5 Jan 2012 11:38:10 +0000 (+0200) Subject: dhcpv6: Request message implemented. X-Git-Tag: 2.0_alpha~752 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3fd016f68a3d021d89fa7a1be1fa712e27e4913b;p=framework%2Fconnectivity%2Fconnman.git dhcpv6: Request message implemented. --- diff --git a/gdhcp/client.c b/gdhcp/client.c index 425f1cb..a8fedb1 100644 --- a/gdhcp/client.c +++ b/gdhcp/client.c @@ -71,6 +71,7 @@ typedef enum _dhcp_client_state { IPV4LL_DEFEND, INFORMATION_REQ, SOLICITATION, + REQUEST, } ClientState; struct _GDHCPClient { @@ -117,6 +118,8 @@ struct _GDHCPClient { gpointer solicitation_data; GDHCPClientEventFunc advertise_cb; gpointer advertise_data; + GDHCPClientEventFunc request_cb; + gpointer request_data; char *last_address; unsigned char *duid; int duid_len; @@ -779,6 +782,11 @@ static int send_solicitation(GDHCPClient *dhcp_client) return send_dhcpv6_msg(dhcp_client, DHCPV6_SOLICIT, "solicit"); } +static int send_dhcpv6_request(GDHCPClient *dhcp_client) +{ + return send_dhcpv6_msg(dhcp_client, DHCPV6_REQUEST, "request"); +} + static int send_information_req(GDHCPClient *dhcp_client) { return send_dhcpv6_msg(dhcp_client, DHCPV6_INFORMATION_REQ, @@ -1957,6 +1965,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, } break; case INFORMATION_REQ: + case REQUEST: if (dhcp_client->type != G_DHCP_IPV6) return TRUE; @@ -1967,7 +1976,10 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, option_len = 0; server_id = dhcpv6_get_option(packet6, pkt_len, G_DHCPV6_SERVERID, &option_len, &count); - if (server_id == NULL || count != 1 || option_len == 0) { + if (server_id == NULL || count != 1 || option_len == 0 || + (dhcp_client->server_duid_len > 0 && + memcmp(dhcp_client->server_duid, server_id, + dhcp_client->server_duid_len) != 0)) { /* RFC 3315, 15.10 */ debug(dhcp_client, "server duid error, discarding msg %p/%d/%d", @@ -1990,6 +2002,11 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, dhcp_client->information_req_data); return TRUE; } + if (dhcp_client->request_cb != NULL) { + dhcp_client->request_cb(dhcp_client, + dhcp_client->request_data); + return TRUE; + } break; default: break; @@ -2103,6 +2120,16 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address) return re; } send_solicitation(dhcp_client); + + } else if (dhcp_client->request_cb) { + dhcp_client->state = REQUEST; + 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_request(dhcp_client); } return 0; @@ -2235,6 +2262,12 @@ void g_dhcp_client_register_event(GDHCPClient *dhcp_client, dhcp_client->advertise_cb = func; dhcp_client->advertise_data = data; return; + case G_DHCP_CLIENT_EVENT_REQUEST: + if (dhcp_client->type == G_DHCP_IPV4) + return; + dhcp_client->request_cb = func; + dhcp_client->request_data = data; + return; } } @@ -2272,6 +2305,7 @@ char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client) case IPV4LL_ANNOUNCE: case INFORMATION_REQ: case SOLICITATION: + case REQUEST: break; } return NULL; diff --git a/gdhcp/gdhcp.h b/gdhcp/gdhcp.h index 5684854..22a28a5 100644 --- a/gdhcp/gdhcp.h +++ b/gdhcp/gdhcp.h @@ -55,6 +55,7 @@ typedef enum { G_DHCP_CLIENT_EVENT_INFORMATION_REQ, G_DHCP_CLIENT_EVENT_SOLICITATION, G_DHCP_CLIENT_EVENT_ADVERTISE, + G_DHCP_CLIENT_EVENT_REQUEST, } GDHCPClientEvent; typedef enum { @@ -82,6 +83,13 @@ typedef enum { #define G_DHCPV6_DNS_SERVERS 23 #define G_DHCPV6_SNTP_SERVERS 31 +#define G_DHCPV6_ERROR_SUCCESS 0 +#define G_DHCPV6_ERROR_FAILURE 1 +#define G_DHCPV6_ERROR_NO_ADDR 2 +#define G_DHCPV6_ERROR_BINDING 3 +#define G_DHCPV6_ERROR_LINK 4 +#define G_DHCPV6_ERROR_MCAST 5 + typedef enum { G_DHCPV6_DUID_LLT = 1, G_DHCPV6_DUID_EN = 2, diff --git a/src/dhcpv6.c b/src/dhcpv6.c index a19eee0..34395fe 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -43,6 +43,9 @@ #define SOL_MAX_DELAY (1 * 1000) #define SOL_TIMEOUT (1 * 1000) #define SOL_MAX_RT (120 * 1000) +#define REQ_TIMEOUT (1 * 1000) +#define REQ_MAX_RT (30 * 1000) +#define REQ_MAX_RC 10 struct connman_dhcpv6 { @@ -58,10 +61,13 @@ struct connman_dhcpv6 { guint RT; /* in msec */ gboolean use_ta; /* set to TRUE if IPv6 privacy is enabled */ GSList *prefixes; /* network prefixes from radvd */ + int request_count; /* how many times REQUEST have been sent */ }; static GHashTable *network_table; +static int dhcpv6_request(struct connman_dhcpv6 *dhcp, gboolean add_addresses); + static inline float get_random() { return (rand() % 200 - 100) / 1000.0; @@ -214,6 +220,10 @@ static void clear_callbacks(GDHCPClient *dhcp_client) NULL, NULL); g_dhcp_client_register_event(dhcp_client, + G_DHCP_CLIENT_EVENT_REQUEST, + NULL, NULL); + + g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_INFORMATION_REQ, NULL, NULL); } @@ -487,6 +497,100 @@ static int set_addresses(GDHCPClient *dhcp_client, return 0; } +static void re_cb(GDHCPClient *dhcp_client, gpointer user_data) +{ + struct connman_dhcpv6 *dhcp = user_data; + uint16_t status; + int ret; + + ret = set_addresses(dhcp_client, dhcp); + + status = g_dhcpv6_client_get_status(dhcp_client); + + DBG("dhcpv6 cb msg %p ret %d status %d", dhcp, ret, status); + + if (ret < 0) { + if (dhcp->callback != NULL) + dhcp->callback(dhcp->network, FALSE); + return; + } + + if (status == G_DHCPV6_ERROR_BINDING) { + /* RFC 3315, 18.1.8 */ + dhcpv6_request(dhcp, FALSE); + } else { + if (dhcp->callback != NULL) + dhcp->callback(dhcp->network, + status == 0 ? TRUE : FALSE); + } +} + +static void request_cb(GDHCPClient *dhcp_client, gpointer user_data) +{ + DBG(""); + + re_cb(dhcp_client, user_data); +} + +static int dhcpv6_request(struct connman_dhcpv6 *dhcp, + gboolean add_addresses) +{ + GDHCPClient *dhcp_client; + uint32_t T1, T2; + + DBG("dhcp %p add %d", dhcp, add_addresses); + + 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); + 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, add_addresses); + + clear_callbacks(dhcp_client); + + g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST, + request_cb, dhcp); + + dhcp->dhcp_client = dhcp_client; + + return g_dhcp_client_start(dhcp_client, NULL); +} + +static gboolean timeout_request(gpointer user_data) +{ + struct connman_dhcpv6 *dhcp = user_data; + + if (dhcp->request_count >= REQ_MAX_RC) { + DBG("max request retry attempts %d", dhcp->request_count); + dhcp->request_count = 0; + if (dhcp->callback != NULL) + dhcp->callback(dhcp->network, FALSE); + return FALSE; + } + + dhcp->request_count++; + + dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT); + DBG("request RT timeout %d msec", dhcp->RT); + dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp); + + g_dhcp_client_start(dhcp->dhcp_client, NULL); + + return FALSE; +} + static int dhcpv6_release(struct connman_dhcpv6 *dhcp) { DBG("dhcp %p", dhcp); @@ -583,6 +687,25 @@ static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data) struct connman_dhcpv6 *dhcp = user_data; DBG("dhcpv6 advertise msg %p", dhcp); + + if (dhcp->timeout > 0) { + g_source_remove(dhcp->timeout); + dhcp->timeout = 0; + } + + if (g_dhcpv6_client_get_status(dhcp_client) != 0) { + if (dhcp->callback != NULL) + dhcp->callback(dhcp->network, FALSE); + return; + } + + dhcp->RT = REQ_TIMEOUT * (1 + get_random()); + DBG("request initial RT timeout %d msec", dhcp->RT); + dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp); + + dhcp->request_count = 1; + + dhcpv6_request(dhcp, TRUE); } static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)