X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gdhcp%2Fclient.c;h=ec1b2a2cd80497bcf2b53dcf78fc3594d7bfa7f7;hb=af22afbaf54aaadb85d6df5d60c5b43f37ea5fc0;hp=425f1cbfcfa06359bc0dcb1b1c29cd68e9ff4bda;hpb=195007a210055b96f42f29348cbce02915dea242;p=framework%2Fconnectivity%2Fconnman.git diff --git a/gdhcp/client.c b/gdhcp/client.c index 425f1cb..ec1b2a2 100644 --- a/gdhcp/client.c +++ b/gdhcp/client.c @@ -2,7 +2,7 @@ * * DHCP client library with GLib integration * - * Copyright (C) 2009-2011 Intel Corporation. All rights reserved. + * Copyright (C) 2009-2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -71,6 +71,10 @@ typedef enum _dhcp_client_state { IPV4LL_DEFEND, INFORMATION_REQ, SOLICITATION, + REQUEST, + RENEW, + REBIND, + RELEASE, } ClientState; struct _GDHCPClient { @@ -84,6 +88,7 @@ struct _GDHCPClient { uint32_t server_ip; uint32_t requested_ip; char *assigned_ip; + time_t start; uint32_t lease_seconds; ListenMode listen_mode; int listener_sockfd; @@ -117,6 +122,14 @@ struct _GDHCPClient { gpointer solicitation_data; GDHCPClientEventFunc advertise_cb; gpointer advertise_data; + GDHCPClientEventFunc request_cb; + gpointer request_data; + GDHCPClientEventFunc renew_cb; + gpointer renew_data; + GDHCPClientEventFunc rebind_cb; + gpointer rebind_data; + GDHCPClientEventFunc release_cb; + gpointer release_data; char *last_address; unsigned char *duid; int duid_len; @@ -127,6 +140,9 @@ struct _GDHCPClient { uint32_t T1, T2; struct in6_addr ia_na; 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, ...) @@ -324,6 +340,17 @@ static void add_send_options(GDHCPClient *dhcp_client, add_binary_option, packet); } +/* + * Return an RFC 951- and 2131-complaint BOOTP 'secs' value that + * represents the number of seconds elapsed from the start of + * attempting DHCP to satisfy some DHCP servers that allow for an + * "authoritative" reply before responding. + */ +static uint16_t dhcp_attempt_secs(GDHCPClient *dhcp_client) +{ + return htons(MIN(time(NULL) - dhcp_client->start, UINT16_MAX)); +} + static int send_discover(GDHCPClient *dhcp_client, uint32_t requested) { struct dhcp_packet packet; @@ -333,6 +360,7 @@ static int send_discover(GDHCPClient *dhcp_client, uint32_t requested) init_packet(dhcp_client, &packet, DHCPDISCOVER); packet.xid = dhcp_client->xid; + packet.secs = dhcp_attempt_secs(dhcp_client); if (requested) dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); @@ -359,6 +387,7 @@ static int send_select(GDHCPClient *dhcp_client) init_packet(dhcp_client, &packet, DHCPREQUEST); packet.xid = dhcp_client->xid; + packet.secs = dhcp_attempt_secs(dhcp_client); dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, dhcp_client->requested_ip); @@ -555,7 +584,7 @@ int g_dhcpv6_create_duid(GDHCPDuidType duid_type, int index, int type, get_interface_mac_address(index, &(*duid)[2 + 2 + 4]); (*duid)[2] = 0; (*duid)[3] = type; - duid_time = time(0) - DUID_TIME_EPOCH; + duid_time = time(NULL) - DUID_TIME_EPOCH; (*duid)[4] = duid_time >> 24; (*duid)[5] = duid_time >> 16; (*duid)[6] = duid_time >> 8; @@ -615,7 +644,9 @@ 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, time_t *last_rebind, + time_t *expire) { if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4) return -EINVAL; @@ -626,6 +657,15 @@ 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; + + if (last_rebind != NULL) + *last_rebind = dhcp_client->last_rebind; + + if (expire != NULL) + *expire = dhcp_client->expire; + return 0; } @@ -779,6 +819,26 @@ 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_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_dhcpv6_release(GDHCPClient *dhcp_client) +{ + return send_dhcpv6_msg(dhcp_client, DHCPV6_RELEASE, "release"); +} + static int send_information_req(GDHCPClient *dhcp_client) { return send_dhcpv6_msg(dhcp_client, DHCPV6_INFORMATION_REQ, @@ -850,6 +910,8 @@ 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 = dhcp_client->last_rebind = time(NULL); + dhcp_client->expire = 0; *error = G_DHCP_CLIENT_ERROR_NONE; @@ -1064,7 +1126,6 @@ static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client) int target_conflict; memset(&arp, 0, sizeof(arp)); - bytes = 0; bytes = read(dhcp_client->listener_sockfd, &arp, sizeof(arp)); if (bytes < 0) return bytes; @@ -1534,6 +1595,9 @@ static GList *get_addresses(GDHCPClient *dhcp_client, uint8_t *option; char *str; + if (value == NULL || len < 4) + return NULL; + iaid = get_uint32(&value[0]); if (dhcp_client->iaid != iaid) return NULL; @@ -1622,6 +1686,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; @@ -1636,6 +1702,9 @@ static GList *get_dhcpv6_option_value_list(GDHCPClient *dhcp_client, char *str; int i; + if (value == NULL) + return NULL; + switch (code) { case G_DHCPV6_DNS_SERVERS: /* RFC 3646, chapter 3 */ case G_DHCPV6_SNTP_SERVERS: /* RFC 4075, chapter 4 */ @@ -1783,7 +1852,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, re = dhcp_recv_l3_packet(&packet, dhcp_client->listener_sockfd); } else if (dhcp_client->listen_mode == L_ARP) { - re = ipv4ll_recv_arp_packet(dhcp_client); + ipv4ll_recv_arp_packet(dhcp_client); return TRUE; } else @@ -1796,6 +1865,9 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, return TRUE; if (dhcp_client->type == G_DHCP_IPV6) { + if (packet6 == NULL) + return TRUE; + count = 0; client_id = dhcpv6_get_option(packet6, pkt_len, G_DHCPV6_CLIENTID, &option_len, &count); @@ -1828,8 +1900,11 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, } else dhcp_client->status_code = 0; - } else + } else { message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE); + if (message_type == NULL) + return TRUE; + } if (message_type == NULL && client_id == NULL) /* No message type / client id option, ignore package */ @@ -1957,6 +2032,10 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, } break; case INFORMATION_REQ: + case REQUEST: + case RENEW: + case REBIND: + case RELEASE: if (dhcp_client->type != G_DHCP_IPV6) return TRUE; @@ -1967,7 +2046,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 +2072,26 @@ 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; + } + if (dhcp_client->renew_cb != NULL) { + dhcp_client->renew_cb(dhcp_client, + dhcp_client->renew_data); + return TRUE; + } + if (dhcp_client->rebind_cb != NULL) { + dhcp_client->rebind_cb(dhcp_client, + dhcp_client->rebind_data); + return TRUE; + } + if (dhcp_client->release_cb != NULL) { + dhcp_client->release_cb(dhcp_client, + dhcp_client->release_data); + return TRUE; + } break; default: break; @@ -2103,6 +2205,46 @@ 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); + + } 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); + + } 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); + + } else if (dhcp_client->release_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_release(dhcp_client); } return 0; @@ -2123,6 +2265,7 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address) return re; dhcp_client->xid = rand(); + dhcp_client->start = time(NULL); } if (last_address == NULL) { @@ -2235,6 +2378,30 @@ 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; + 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; + 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; + case G_DHCP_CLIENT_EVENT_RELEASE: + if (dhcp_client->type == G_DHCP_IPV4) + return; + dhcp_client->release_cb = func; + dhcp_client->release_data = data; + return; } } @@ -2272,6 +2439,10 @@ char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client) case IPV4LL_ANNOUNCE: case INFORMATION_REQ: case SOLICITATION: + case REQUEST: + case RENEW: + case REBIND: + case RELEASE: break; } return NULL; @@ -2300,19 +2471,53 @@ void g_dhcp_client_clear_values(GDHCPClient *dhcp_client) g_hash_table_remove_all(dhcp_client->send_value_hash); } -static uint8_t *alloc_dhcp_option(int code, const char *str, int extra) +static uint8_t *alloc_dhcp_option(int code, const uint8_t *data, unsigned size) { uint8_t *storage; - int len = strnlen(str, 255); - storage = malloc(len + extra + OPT_DATA); + storage = g_try_malloc(size + OPT_DATA); + if (storage == NULL) + return NULL; + storage[OPT_CODE] = code; - storage[OPT_LEN] = len + extra; - memcpy(storage + extra + OPT_DATA, str, len); + storage[OPT_LEN] = size; + memcpy(&storage[OPT_DATA], data, size); return storage; } +static uint8_t *alloc_dhcp_data_option(int code, const uint8_t *data, unsigned size) +{ + return alloc_dhcp_option(code, data, MIN(size, 255)); +} + +static uint8_t *alloc_dhcp_string_option(int code, const char *str) +{ + return alloc_dhcp_data_option(code, (const uint8_t *)str, strlen(str)); +} + +GDHCPClientError g_dhcp_client_set_id(GDHCPClient *dhcp_client) +{ + const unsigned maclen = 6; + const unsigned idlen = maclen + 1; + const uint8_t option_code = G_DHCP_CLIENT_ID; + uint8_t idbuf[idlen]; + uint8_t *data_option; + + idbuf[0] = ARPHRD_ETHER; + + memcpy(&idbuf[1], dhcp_client->mac_address, maclen); + + data_option = alloc_dhcp_data_option(option_code, idbuf, idlen); + if (data_option == NULL) + return G_DHCP_CLIENT_ERROR_NOMEM; + + g_hash_table_insert(dhcp_client->send_value_hash, + GINT_TO_POINTER((int) option_code), data_option); + + return G_DHCP_CLIENT_ERROR_NONE; +} + /* Now only support send hostname */ GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client, unsigned char option_code, const char *option_value) @@ -2320,8 +2525,10 @@ GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client, uint8_t *binary_option; if (option_code == G_DHCP_HOST_NAME && option_value != NULL) { - binary_option = alloc_dhcp_option(option_code, - option_value, 0); + binary_option = alloc_dhcp_string_option(option_code, + option_value); + if (binary_option == NULL) + return G_DHCP_CLIENT_ERROR_NOMEM; g_hash_table_insert(dhcp_client->send_value_hash, GINT_TO_POINTER((int) option_code), binary_option); @@ -2368,6 +2575,30 @@ 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(NULL); +} + +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(NULL); +} + +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(NULL) + timeout; +} + uint16_t g_dhcpv6_client_get_status(GDHCPClient *dhcp_client) { if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)