X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gdhcp%2Fclient.c;h=327034656912e719ceb682fc1fbb944583467fcd;hb=fa54ada1a44c97165690e548719231de23756e74;hp=45723d7300252b11e925951070a7947ca951ea3a;hpb=0c96e020cd279cba67274d20d7895939e52d1593;p=framework%2Fconnectivity%2Fconnman.git diff --git a/gdhcp/client.c b/gdhcp/client.c index 45723d7..3270346 100644 --- a/gdhcp/client.c +++ b/gdhcp/client.c @@ -23,6 +23,7 @@ #include #endif +#define _GNU_SOURCE #include #include #include @@ -32,8 +33,8 @@ #include #include +#include #include -#include #include #include @@ -42,17 +43,19 @@ #include "gdhcp.h" #include "common.h" +#include "ipv4ll.h" #define DISCOVER_TIMEOUT 3 -#define DISCOVER_RETRIES 3 +#define DISCOVER_RETRIES 10 #define REQUEST_TIMEOUT 3 -#define REQUEST_RETRIES 3 +#define REQUEST_RETRIES 5 typedef enum _listen_mode { L_NONE, L2, L3, + L_ARP, } ListenMode; typedef enum _dhcp_client_state { @@ -62,10 +65,14 @@ typedef enum _dhcp_client_state { RENEWING, REBINDING, RELEASED, + IPV4LL_PROBE, + IPV4LL_ANNOUNCE, + IPV4LL_MONITOR, + IPV4LL_DEFEND, } ClientState; struct _GDHCPClient { - gint ref_count; + int ref_count; GDHCPType type; ClientState state; int ifindex; @@ -80,6 +87,7 @@ struct _GDHCPClient { int listener_sockfd; uint8_t retry_times; uint8_t ack_retry_times; + uint8_t conflicts; guint timeout; guint listener_watch; GIOChannel *listener_channel; @@ -89,18 +97,21 @@ struct _GDHCPClient { GHashTable *send_value_hash; GDHCPClientEventFunc lease_available_cb; gpointer lease_available_data; + GDHCPClientEventFunc ipv4ll_available_cb; + gpointer ipv4ll_available_data; GDHCPClientEventFunc no_lease_cb; gpointer no_lease_data; GDHCPClientEventFunc lease_lost_cb; gpointer lease_lost_data; + GDHCPClientEventFunc ipv4ll_lost_cb; + gpointer ipv4ll_lost_data; GDHCPClientEventFunc address_conflict_cb; gpointer address_conflict_data; GDHCPDebugFunc debug_func; gpointer debug_data; + char *last_address; }; -static GTimer *timer = NULL; - static inline void debug(GDHCPClient *client, const char *format, ...) { char str[256]; @@ -192,7 +203,6 @@ static int send_discover(GDHCPClient *dhcp_client, uint32_t requested) static int send_select(GDHCPClient *dhcp_client) { struct dhcp_packet packet; - struct in_addr addr; debug(dhcp_client, "sending DHCP select request"); @@ -208,8 +218,6 @@ static int send_select(GDHCPClient *dhcp_client) add_send_options(dhcp_client, &packet); - addr.s_addr = dhcp_client->requested_ip; - return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, SERVER_PORT, MAC_BCAST_ADDR, dhcp_client->ifindex); @@ -270,69 +278,82 @@ static int send_release(GDHCPClient *dhcp_client, server, SERVER_PORT); } -static gboolean interface_is_up(int index) +static gboolean ipv4ll_probe_timeout(gpointer dhcp_data); +static int switch_listening_mode(GDHCPClient *dhcp_client, + ListenMode listen_mode); + +static gboolean send_probe_packet(gpointer dhcp_data) { - int sk, err; - struct ifreq ifr; - gboolean ret = FALSE; + GDHCPClient *dhcp_client; + guint timeout; - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) { - perror("Open socket error"); - return FALSE; + dhcp_client = dhcp_data; + /* if requested_ip is not valid, pick a new address*/ + if (dhcp_client->requested_ip == 0) { + debug(dhcp_client, "pick a new random address"); + dhcp_client->requested_ip = ipv4ll_random_ip(0); } - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_ifindex = index; - - err = ioctl(sk, SIOCGIFNAME, &ifr); - if (err < 0) { - perror("Get interface name error"); - goto done; - } + debug(dhcp_client, "sending IPV4LL probe request"); - err = ioctl(sk, SIOCGIFFLAGS, &ifr); - if (err < 0) { - perror("Get interface flags error"); - goto done; + if (dhcp_client->retry_times == 1) { + dhcp_client->state = IPV4LL_PROBE; + switch_listening_mode(dhcp_client, L_ARP); } - - if (ifr.ifr_flags & IFF_UP) - ret = TRUE; - -done: - close(sk); - - return ret; + ipv4ll_send_arp_packet(dhcp_client->mac_address, 0, + dhcp_client->requested_ip, dhcp_client->ifindex); + + if (dhcp_client->retry_times < PROBE_NUM) { + /*add a random timeout in range of PROBE_MIN to PROBE_MAX*/ + timeout = ipv4ll_random_delay_ms(PROBE_MAX-PROBE_MIN); + timeout += PROBE_MIN*1000; + } else + timeout = (ANNOUNCE_WAIT * 1000); + + dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH, + timeout, + ipv4ll_probe_timeout, + dhcp_client, + NULL); + return FALSE; } -static char *get_interface_name(int index) -{ - struct ifreq ifr; - int sk, err; +static gboolean ipv4ll_announce_timeout(gpointer dhcp_data); +static gboolean ipv4ll_defend_timeout(gpointer dhcp_data); - if (index < 0) - return NULL; +static gboolean send_announce_packet(gpointer dhcp_data) +{ + GDHCPClient *dhcp_client; - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) { - perror("Open socket error"); - return NULL; - } + dhcp_client = dhcp_data; - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_ifindex = index; + debug(dhcp_client, "sending IPV4LL announce request"); - err = ioctl(sk, SIOCGIFNAME, &ifr); - if (err < 0) { - perror("Get interface name error"); - close(sk); - return NULL; - } + ipv4ll_send_arp_packet(dhcp_client->mac_address, + dhcp_client->requested_ip, + dhcp_client->requested_ip, + dhcp_client->ifindex); - close(sk); + if (dhcp_client->timeout > 0) + g_source_remove(dhcp_client->timeout); + dhcp_client->timeout = 0; - return g_strdup(ifr.ifr_name); + if (dhcp_client->state == IPV4LL_DEFEND) { + dhcp_client->timeout = + g_timeout_add_seconds_full(G_PRIORITY_HIGH, + DEFEND_INTERVAL, + ipv4ll_defend_timeout, + dhcp_client, + NULL); + return TRUE; + } else + dhcp_client->timeout = + g_timeout_add_seconds_full(G_PRIORITY_HIGH, + ANNOUNCE_INTERVAL, + ipv4ll_announce_timeout, + dhcp_client, + NULL); + return TRUE; } static void get_interface_mac_address(int index, uint8_t *mac_address) @@ -340,7 +361,7 @@ static void get_interface_mac_address(int index, uint8_t *mac_address) struct ifreq ifr; int sk, err; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) { perror("Open socket error"); return; @@ -416,8 +437,10 @@ GDHCPClient *g_dhcp_client_new(GDHCPType type, dhcp_client->type = type; dhcp_client->ifindex = ifindex; dhcp_client->lease_available_cb = NULL; + dhcp_client->ipv4ll_available_cb = NULL; dhcp_client->no_lease_cb = NULL; dhcp_client->lease_lost_cb = NULL; + dhcp_client->ipv4ll_lost_cb = NULL; dhcp_client->address_conflict_cb = NULL; dhcp_client->listener_watch = 0; dhcp_client->retry_times = 0; @@ -492,7 +515,7 @@ static int dhcp_l2_socket(int ifindex) .filter = (struct sock_filter *) filter_instr, }; - fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP)); if (fd < 0) return fd; @@ -501,6 +524,7 @@ static int dhcp_l2_socket(int ifindex) setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)); + memset(&sock, 0, sizeof(sock)); sock.sll_family = AF_PACKET; sock.sll_protocol = htons(ETH_P_IP); sock.sll_ifindex = ifindex; @@ -581,6 +605,131 @@ static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd) return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); } +static void ipv4ll_start(GDHCPClient *dhcp_client) +{ + guint timeout; + int seed; + + if (dhcp_client->timeout > 0) { + g_source_remove(dhcp_client->timeout); + dhcp_client->timeout = 0; + } + + switch_listening_mode(dhcp_client, L_NONE); + dhcp_client->type = G_DHCP_IPV4LL; + dhcp_client->retry_times = 0; + dhcp_client->requested_ip = 0; + + /*try to start with a based mac address ip*/ + seed = (dhcp_client->mac_address[4] << 8 | dhcp_client->mac_address[4]); + dhcp_client->requested_ip = ipv4ll_random_ip(seed); + + /*first wait a random delay to avoid storm of arp request on boot*/ + timeout = ipv4ll_random_delay_ms(PROBE_WAIT); + + dhcp_client->retry_times++; + dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH, + timeout, + send_probe_packet, + dhcp_client, + NULL); +} + +static void ipv4ll_stop(GDHCPClient *dhcp_client) +{ + + switch_listening_mode(dhcp_client, L_NONE); + + if (dhcp_client->timeout > 0) + g_source_remove(dhcp_client->timeout); + + if (dhcp_client->listener_watch > 0) { + g_source_remove(dhcp_client->listener_watch); + dhcp_client->listener_watch = 0; + } + + dhcp_client->state = IPV4LL_PROBE; + dhcp_client->retry_times = 0; + dhcp_client->requested_ip = 0; + + g_free(dhcp_client->assigned_ip); + dhcp_client->assigned_ip = NULL; +} + +static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client) +{ + int bytes; + struct ether_arp arp; + uint32_t ip_requested; + int source_conflict; + int target_conflict; + + memset(&arp, 0, sizeof(arp)); + bytes = 0; + bytes = read(dhcp_client->listener_sockfd, &arp, sizeof(arp)); + if (bytes < 0) + return bytes; + + if (arp.arp_op != htons(ARPOP_REPLY) && + arp.arp_op != htons(ARPOP_REQUEST)) + return -EINVAL; + + ip_requested = ntohl(dhcp_client->requested_ip); + source_conflict = !memcmp(arp.arp_spa, &ip_requested, + sizeof(ip_requested)); + + target_conflict = !memcmp(arp.arp_tpa, &ip_requested, + sizeof(ip_requested)); + + if (!source_conflict && !target_conflict) + return 0; + + dhcp_client->conflicts++; + + debug(dhcp_client, "IPV4LL conflict detected"); + + if (dhcp_client->state == IPV4LL_MONITOR) { + if (!source_conflict) + return 0; + dhcp_client->state = IPV4LL_DEFEND; + debug(dhcp_client, "DEFEND mode conflicts : %d", + dhcp_client->conflicts); + /*Try to defend with a single announce*/ + send_announce_packet(dhcp_client); + return 0; + } + + if (dhcp_client->state == IPV4LL_DEFEND) { + if (!source_conflict) + return 0; + else if (dhcp_client->ipv4ll_lost_cb != NULL) + dhcp_client->ipv4ll_lost_cb(dhcp_client, + dhcp_client->ipv4ll_lost_data); + } + + ipv4ll_stop(dhcp_client); + + if (dhcp_client->conflicts < MAX_CONFLICTS) { + /*restart whole state machine*/ + dhcp_client->retry_times++; + dhcp_client->timeout = + g_timeout_add_full(G_PRIORITY_HIGH, + ipv4ll_random_delay_ms(PROBE_WAIT), + send_probe_packet, + dhcp_client, + NULL); + } + /* Here we got a lot of conflicts, RFC3927 states that we have + * to wait RATE_LIMIT_INTERVAL before retrying, + * but we just report failure. + */ + else if (dhcp_client->no_lease_cb != NULL) + dhcp_client->no_lease_cb(dhcp_client, + dhcp_client->no_lease_data); + + return 0; +} + static gboolean check_package_owner(GDHCPClient *dhcp_client, struct dhcp_packet *packet) { @@ -602,6 +751,9 @@ static gboolean request_timeout(gpointer user_data) { GDHCPClient *dhcp_client = user_data; + debug(dhcp_client, "request timeout (retries %d)", + dhcp_client->retry_times); + dhcp_client->retry_times++; start_request(dhcp_client); @@ -618,6 +770,9 @@ static int switch_listening_mode(GDHCPClient *dhcp_client, GIOChannel *listener_channel; int listener_sockfd; + debug(dhcp_client, "switch listening mode (%d ==> %d)", + dhcp_client->listen_mode, listen_mode); + if (dhcp_client->listen_mode == listen_mode) return 0; @@ -637,6 +792,8 @@ static int switch_listening_mode(GDHCPClient *dhcp_client, else if (listen_mode == L3) listener_sockfd = dhcp_l3_socket(CLIENT_PORT, dhcp_client->interface); + else if (listen_mode == L_ARP) + listener_sockfd = ipv4ll_arp_socket(dhcp_client->ifindex); else return -EIO; @@ -656,8 +813,8 @@ static int switch_listening_mode(GDHCPClient *dhcp_client, g_io_channel_set_close_on_unref(listener_channel, TRUE); dhcp_client->listener_watch = - g_io_add_watch_full(listener_channel, - G_PRIORITY_HIGH, G_IO_IN, + g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH, + G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP, listener_event, dhcp_client, NULL); g_io_channel_unref(dhcp_client->listener_channel); @@ -667,12 +824,12 @@ static int switch_listening_mode(GDHCPClient *dhcp_client, static void start_request(GDHCPClient *dhcp_client) { + debug(dhcp_client, "start request (retries %d)", + dhcp_client->retry_times); + if (dhcp_client->retry_times == REQUEST_RETRIES) { dhcp_client->state = INIT_SELECTING; - - if (dhcp_client->no_lease_cb != NULL) - dhcp_client->no_lease_cb(dhcp_client, - dhcp_client->no_lease_data); + ipv4ll_start(dhcp_client); return; } @@ -712,6 +869,8 @@ static uint32_t get_lease(struct dhcp_packet *packet) static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times) { + debug(dhcp_client, "restart DHCP (retries %d)", retry_times); + if (dhcp_client->timeout > 0) { g_source_remove(dhcp_client->timeout); dhcp_client->timeout = 0; @@ -721,13 +880,15 @@ static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times) dhcp_client->requested_ip = 0; switch_listening_mode(dhcp_client, L2); - g_dhcp_client_start(dhcp_client); + g_dhcp_client_start(dhcp_client, dhcp_client->last_address); } static gboolean start_rebound_timeout(gpointer user_data) { GDHCPClient *dhcp_client = user_data; + debug(dhcp_client, "start rebound timeout"); + switch_listening_mode(dhcp_client, L2); dhcp_client->lease_seconds >>= 1; @@ -757,6 +918,8 @@ static gboolean start_rebound_timeout(gpointer user_data) static void start_rebound(GDHCPClient *dhcp_client) { + debug(dhcp_client, "start rebound"); + dhcp_client->state = REBINDING; dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH, @@ -769,12 +932,8 @@ static void start_rebound(GDHCPClient *dhcp_client) static gboolean start_renew_timeout(gpointer user_data) { GDHCPClient *dhcp_client = user_data; - gdouble elapse; - gulong microseconds; - - elapse = g_timer_elapsed(timer, µseconds); - g_timer_start(timer); + debug(dhcp_client, "start renew timeout"); dhcp_client->state = RENEWING; @@ -786,6 +945,9 @@ static gboolean start_renew_timeout(gpointer user_data) else { send_renew(dhcp_client); + if (dhcp_client->timeout > 0) + g_source_remove(dhcp_client->timeout); + dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH, dhcp_client->lease_seconds >> 1, @@ -799,13 +961,14 @@ static gboolean start_renew_timeout(gpointer user_data) static void start_bound(GDHCPClient *dhcp_client) { + debug(dhcp_client, "start bound"); + dhcp_client->state = BOUND; - if (timer == NULL) - timer = g_timer_new(); + if (dhcp_client->timeout > 0) + g_source_remove(dhcp_client->timeout); - dhcp_client->timeout = - g_timeout_add_seconds_full(G_PRIORITY_HIGH, + dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH, dhcp_client->lease_seconds >> 1, start_renew_timeout, dhcp_client, NULL); @@ -815,6 +978,8 @@ static gboolean restart_dhcp_timeout(gpointer user_data) { GDHCPClient *dhcp_client = user_data; + debug(dhcp_client, "restart DHCP timeout"); + dhcp_client->ack_retry_times++; restart_dhcp(dhcp_client, dhcp_client->ack_retry_times); @@ -899,20 +1064,26 @@ static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type) return ret; } -static GList *get_option_value_list(char *value) +static GList *get_option_value_list(char *value, GDHCPOptionType type) { char *pos = value; GList *list = NULL; + if (pos == NULL) + return NULL; + + if (type == OPTION_STRING) + return g_list_append(list, g_strdup(value)); + while ((pos = strchr(pos, ' ')) != NULL) { *pos = '\0'; - list = g_list_append(list, g_strdup(value)); + list = g_list_append(list, g_strdup(value)); value = ++pos; } - list = g_list_append(list, g_strdup(value)); + list = g_list_append(list, g_strdup(value)); return list; } @@ -942,7 +1113,7 @@ static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet) g_hash_table_remove(dhcp_client->code_value_hash, GINT_TO_POINTER((int) code)); - value_list = get_option_value_list(option_value); + value_list = get_option_value_list(option_value, type); g_free(option_value); @@ -975,6 +1146,10 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, re = dhcp_recv_l2_packet(&packet, dhcp_client->listener_sockfd); else if (dhcp_client->listen_mode == L3) 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); + return TRUE; + } else re = -EIO; @@ -986,9 +1161,12 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE); if (message_type == NULL) - /* No message type option, ignore pakcage */ + /* No message type option, ignore package */ return TRUE; + debug(dhcp_client, "received DHCP packet (current state %d)", + dhcp_client->state); + switch (dhcp_client->state) { case INIT_SELECTING: if (*message_type != DHCPOFFER) @@ -1051,6 +1229,9 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, break; } + debug(dhcp_client, "processed DHCP packet (new state %d)", + dhcp_client->state); + return TRUE; } @@ -1060,20 +1241,84 @@ static gboolean discover_timeout(gpointer user_data) dhcp_client->retry_times++; - g_dhcp_client_start(dhcp_client); + /* + * We do not send the REQUESTED IP option if we are retrying because + * if the server is non-authoritative it will ignore the request if the + * option is present. + */ + g_dhcp_client_start(dhcp_client, NULL); + + return FALSE; +} + +static gboolean ipv4ll_defend_timeout(gpointer dhcp_data) +{ + GDHCPClient *dhcp_client = dhcp_data; + + debug(dhcp_client, "back to MONITOR mode"); + + dhcp_client->conflicts = 0; + dhcp_client->state = IPV4LL_MONITOR; + + return FALSE; +} + +static gboolean ipv4ll_announce_timeout(gpointer dhcp_data) +{ + GDHCPClient *dhcp_client = dhcp_data; + uint32_t ip; + + debug(dhcp_client, "request timeout (retries %d)", + dhcp_client->retry_times); + + if (dhcp_client->retry_times != ANNOUNCE_NUM){ + dhcp_client->retry_times++; + send_announce_packet(dhcp_client); + return FALSE; + } + + ip = htonl(dhcp_client->requested_ip); + debug(dhcp_client, "switching to monitor mode"); + dhcp_client->state = IPV4LL_MONITOR; + dhcp_client->assigned_ip = get_ip(ip); + + if (dhcp_client->ipv4ll_available_cb != NULL) + dhcp_client->ipv4ll_available_cb(dhcp_client, + dhcp_client->ipv4ll_available_data); + dhcp_client->conflicts = 0; return FALSE; } -int g_dhcp_client_start(GDHCPClient *dhcp_client) +static gboolean ipv4ll_probe_timeout(gpointer dhcp_data) +{ + + GDHCPClient *dhcp_client = dhcp_data; + + debug(dhcp_client, "IPV4LL probe timeout (retries %d)", + dhcp_client->retry_times); + + if (dhcp_client->retry_times == PROBE_NUM) { + dhcp_client->state = IPV4LL_ANNOUNCE; + dhcp_client->retry_times = 0; + + dhcp_client->retry_times++; + send_announce_packet(dhcp_client); + return FALSE; + } + dhcp_client->retry_times++; + send_probe_packet(dhcp_client); + + return FALSE; +} + +int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address) { int re; + uint32_t addr; if (dhcp_client->retry_times == DISCOVER_RETRIES) { - if (dhcp_client->no_lease_cb != NULL) - dhcp_client->no_lease_cb(dhcp_client, - dhcp_client->no_lease_data); - + ipv4ll_start(dhcp_client); return 0; } @@ -1089,7 +1334,18 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client) dhcp_client->xid = rand(); } - send_discover(dhcp_client, 0); + if (last_address == NULL) { + addr = 0; + } else { + addr = inet_addr(last_address); + if (addr == 0xFFFFFFFF) { + addr = 0; + } else { + g_free(dhcp_client->last_address); + dhcp_client->last_address = g_strdup(last_address); + } + } + send_discover(dhcp_client, addr); dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH, DISCOVER_TIMEOUT, @@ -1146,6 +1402,10 @@ void g_dhcp_client_register_event(GDHCPClient *dhcp_client, dhcp_client->lease_available_cb = func; dhcp_client->lease_available_data = data; return; + case G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE: + dhcp_client->ipv4ll_available_cb = func; + dhcp_client->ipv4ll_available_data = data; + return; case G_DHCP_CLIENT_EVENT_NO_LEASE: dhcp_client->no_lease_cb = func; dhcp_client->no_lease_data = data; @@ -1154,6 +1414,10 @@ void g_dhcp_client_register_event(GDHCPClient *dhcp_client, dhcp_client->lease_lost_cb = func; dhcp_client->lease_lost_data = data; return; + case G_DHCP_CLIENT_EVENT_IPV4LL_LOST: + dhcp_client->ipv4ll_lost_cb = func; + dhcp_client->ipv4ll_lost_data = data; + return; case G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT: dhcp_client->address_conflict_cb = func; dhcp_client->address_conflict_data = data; @@ -1171,6 +1435,30 @@ char *g_dhcp_client_get_address(GDHCPClient *dhcp_client) return g_strdup(dhcp_client->assigned_ip); } +char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client) +{ + GList *option = NULL; + + switch (dhcp_client->state) { + case IPV4LL_DEFEND: + case IPV4LL_MONITOR: + return g_strdup("255.255.0.0"); + case BOUND: + case RENEWING: + case REBINDING: + option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET); + if (option != NULL) + return g_strdup(option->data); + case INIT_SELECTING: + case REQUESTING: + case RELEASED: + case IPV4LL_PROBE: + case IPV4LL_ANNOUNCE: + break; + } + return NULL; +} + GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client, unsigned char option_code) { @@ -1196,32 +1484,15 @@ static uint8_t *alloc_dhcp_option(int code, const char *str, int extra) return storage; } -static const char *get_hostname(const char *host) -{ - char local_host_name[HOST_NAME_MAX + 1]; - - if (g_strcmp0("", host) != 0) - return g_strdup(host); - - if (gethostname(local_host_name, HOST_NAME_MAX) != 0) - return NULL; - - local_host_name[HOST_NAME_MAX] = 0; - - return g_strdup(local_host_name); -} - /* Now only support send hostname */ GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client, unsigned char option_code, const char *option_value) { uint8_t *binary_option; - const char *hostname; - - if (option_code == G_DHCP_HOST_NAME) { - hostname = get_hostname(option_value); - binary_option = alloc_dhcp_option(option_code, hostname, 0); + if (option_code == G_DHCP_HOST_NAME && option_value != NULL) { + binary_option = alloc_dhcp_option(option_code, + option_value, 0); g_hash_table_insert(dhcp_client->send_value_hash, GINT_TO_POINTER((int) option_code), binary_option); @@ -1235,7 +1506,7 @@ GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client) if (dhcp_client == NULL) return NULL; - g_atomic_int_inc(&dhcp_client->ref_count); + __sync_fetch_and_add(&dhcp_client->ref_count, 1); return dhcp_client; } @@ -1245,13 +1516,14 @@ void g_dhcp_client_unref(GDHCPClient *dhcp_client) if (dhcp_client == NULL) return; - if (g_atomic_int_dec_and_test(&dhcp_client->ref_count) == FALSE) + if (__sync_fetch_and_sub(&dhcp_client->ref_count, 1) != 1) return; g_dhcp_client_stop(dhcp_client); g_free(dhcp_client->interface); g_free(dhcp_client->assigned_ip); + g_free(dhcp_client->last_address); g_list_free(dhcp_client->request_list); g_list_free(dhcp_client->require_list);