3 * DHCP client library with GLib integration
5 * Copyright (C) 2009-2010 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31 #include <sys/ioctl.h>
32 #include <arpa/inet.h>
34 #include <netpacket/packet.h>
35 #include <net/ethernet.h>
36 #include <net/if_arp.h>
39 #include <linux/filter.h>
46 #define DISCOVER_TIMEOUT 3
47 #define DISCOVER_RETRIES 3
49 #define REQUEST_TIMEOUT 3
50 #define REQUEST_RETRIES 3
52 typedef enum _listen_mode {
58 typedef enum _dhcp_client_state {
73 uint8_t mac_address[6];
76 uint32_t requested_ip;
78 uint32_t lease_seconds;
79 ListenMode listen_mode;
82 uint8_t ack_retry_times;
85 GIOChannel *listener_channel;
88 GHashTable *code_value_hash;
89 GHashTable *send_value_hash;
90 GDHCPClientEventFunc lease_available_cb;
91 gpointer lease_available_data;
92 GDHCPClientEventFunc no_lease_cb;
93 gpointer no_lease_data;
94 GDHCPClientEventFunc lease_lost_cb;
95 gpointer lease_lost_data;
96 GDHCPClientEventFunc address_conflict_cb;
97 gpointer address_conflict_data;
98 GDHCPDebugFunc debug_func;
102 static GTimer *timer = NULL;
104 /* Initialize the packet with the proper defaults */
105 static void init_packet(GDHCPClient *dhcp_client,
106 struct dhcp_packet *packet, char type)
108 dhcp_init_header(packet, type);
110 memcpy(packet->chaddr, dhcp_client->mac_address, 6);
113 static void add_request_options(GDHCPClient *dhcp_client,
114 struct dhcp_packet *packet)
119 int end = dhcp_end_option(packet->options);
121 for (list = dhcp_client->request_list; list; list = list->next) {
122 code = (uint8_t) GPOINTER_TO_INT(list->data);
124 packet->options[end + OPT_DATA + len] = code;
129 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
130 packet->options[end + OPT_LEN] = len;
131 packet->options[end + OPT_DATA + len] = DHCP_END;
135 static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
137 uint8_t *option = value;
138 struct dhcp_packet *packet = user_data;
140 dhcp_add_binary_option(packet, option);
143 static void add_send_options(GDHCPClient *dhcp_client,
144 struct dhcp_packet *packet)
146 g_hash_table_foreach(dhcp_client->send_value_hash,
147 add_binary_option, packet);
150 static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
152 struct dhcp_packet packet;
154 init_packet(dhcp_client, &packet, DHCPDISCOVER);
156 packet.xid = dhcp_client->xid;
159 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
161 /* Explicitly saying that we want RFC-compliant packets helps
162 * some buggy DHCP servers to NOT send bigger packets */
163 dhcp_add_simple_option(&packet, DHCP_MAX_SIZE, htons(576));
165 add_request_options(dhcp_client, &packet);
167 add_send_options(dhcp_client, &packet);
169 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
170 INADDR_BROADCAST, SERVER_PORT,
171 MAC_BCAST_ADDR, dhcp_client->ifindex);
174 static int send_select(GDHCPClient *dhcp_client)
176 struct dhcp_packet packet;
179 init_packet(dhcp_client, &packet, DHCPREQUEST);
181 packet.xid = dhcp_client->xid;
183 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP,
184 dhcp_client->requested_ip);
185 dhcp_add_simple_option(&packet, DHCP_SERVER_ID, dhcp_client->server_ip);
187 add_request_options(dhcp_client, &packet);
189 add_send_options(dhcp_client, &packet);
191 addr.s_addr = dhcp_client->requested_ip;
193 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
194 INADDR_BROADCAST, SERVER_PORT,
195 MAC_BCAST_ADDR, dhcp_client->ifindex);
198 static int send_renew(GDHCPClient *dhcp_client)
200 struct dhcp_packet packet;
202 init_packet(dhcp_client , &packet, DHCPREQUEST);
203 packet.xid = dhcp_client->xid;
204 packet.ciaddr = dhcp_client->requested_ip;
206 add_request_options(dhcp_client, &packet);
208 add_send_options(dhcp_client, &packet);
210 return dhcp_send_kernel_packet(&packet,
211 dhcp_client->requested_ip, CLIENT_PORT,
212 dhcp_client->server_ip, SERVER_PORT);
215 static int send_rebound(GDHCPClient *dhcp_client)
217 struct dhcp_packet packet;
219 init_packet(dhcp_client , &packet, DHCPREQUEST);
220 packet.xid = dhcp_client->xid;
221 packet.ciaddr = dhcp_client->requested_ip;
223 add_request_options(dhcp_client, &packet);
225 add_send_options(dhcp_client, &packet);
227 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
228 INADDR_BROADCAST, SERVER_PORT,
229 MAC_BCAST_ADDR, dhcp_client->ifindex);
232 static int send_release(GDHCPClient *dhcp_client,
233 uint32_t server, uint32_t ciaddr)
235 struct dhcp_packet packet;
237 init_packet(dhcp_client, &packet, DHCPRELEASE);
239 packet.ciaddr = ciaddr;
241 dhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
243 return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
244 server, SERVER_PORT);
247 static gboolean interface_is_up(int index)
251 gboolean ret = FALSE;
253 sk = socket(PF_INET, SOCK_DGRAM, 0);
255 perror("Open socket error");
259 memset(&ifr, 0, sizeof(ifr));
260 ifr.ifr_ifindex = index;
262 err = ioctl(sk, SIOCGIFNAME, &ifr);
264 perror("Get interface name error");
268 err = ioctl(sk, SIOCGIFFLAGS, &ifr);
270 perror("Get interface flags error");
274 if (ifr.ifr_flags & IFF_UP)
283 static char *get_interface_name(int index)
291 sk = socket(PF_INET, SOCK_DGRAM, 0);
293 perror("Open socket error");
297 memset(&ifr, 0, sizeof(ifr));
298 ifr.ifr_ifindex = index;
300 err = ioctl(sk, SIOCGIFNAME, &ifr);
302 perror("Get interface name error");
309 return g_strdup(ifr.ifr_name);
312 static void get_interface_mac_address(int index, uint8_t *mac_address)
317 sk = socket(PF_INET, SOCK_DGRAM, 0);
319 perror("Open socket error");
323 memset(&ifr, 0, sizeof(ifr));
324 ifr.ifr_ifindex = index;
326 err = ioctl(sk, SIOCGIFNAME, &ifr);
328 perror("Get interface name error");
332 err = ioctl(sk, SIOCGIFHWADDR, &ifr);
334 perror("Get mac address error");
338 memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
344 static void remove_value(gpointer data, gpointer user_data)
350 static void remove_option_value(gpointer data)
352 GList *option_value = data;
354 g_list_foreach(option_value, remove_value, NULL);
357 GDHCPClient *g_dhcp_client_new(GDHCPType type,
358 int ifindex, GDHCPClientError *error)
360 GDHCPClient *dhcp_client;
363 *error = G_DHCP_CLIENT_ERROR_INVALID_INDEX;
367 dhcp_client = g_try_new0(GDHCPClient, 1);
368 if (dhcp_client == NULL) {
369 *error = G_DHCP_CLIENT_ERROR_NOMEM;
373 dhcp_client->interface = get_interface_name(ifindex);
374 if (dhcp_client->interface == NULL) {
375 *error = G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE;
379 if (interface_is_up(ifindex) == FALSE) {
380 *error = G_DHCP_CLIENT_ERROR_INTERFACE_DOWN;
384 get_interface_mac_address(ifindex, dhcp_client->mac_address);
386 dhcp_client->listener_sockfd = -1;
387 dhcp_client->listener_channel = NULL;
388 dhcp_client->listen_mode = L_NONE;
389 dhcp_client->ref_count = 1;
390 dhcp_client->type = type;
391 dhcp_client->ifindex = ifindex;
392 dhcp_client->lease_available_cb = NULL;
393 dhcp_client->no_lease_cb = NULL;
394 dhcp_client->lease_lost_cb = NULL;
395 dhcp_client->address_conflict_cb = NULL;
396 dhcp_client->listener_watch = 0;
397 dhcp_client->retry_times = 0;
398 dhcp_client->ack_retry_times = 0;
399 dhcp_client->code_value_hash = g_hash_table_new_full(g_direct_hash,
400 g_direct_equal, NULL, remove_option_value);
401 dhcp_client->send_value_hash = g_hash_table_new_full(g_direct_hash,
402 g_direct_equal, NULL, g_free);
403 dhcp_client->request_list = NULL;
404 dhcp_client->require_list = NULL;
406 *error = G_DHCP_CLIENT_ERROR_NONE;
411 g_free(dhcp_client->interface);
416 #define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68)
418 static int dhcp_l2_socket(int ifindex)
421 struct sockaddr_ll sock;
426 * I've selected not to see LL header, so BPF doesn't see it, too.
427 * The filter may also pass non-IP and non-ARP packets, but we do
428 * a more complete check when receiving the message in userspace.
430 * and filter shamelessly stolen from:
432 * http://www.flamewarmaster.de/software/dhcpclient/
434 * There are a few other interesting ideas on that page (look under
435 * "Motivation"). Use of netlink events is most interesting. Think
436 * of various network servers listening for events and reconfiguring.
437 * That would obsolete sending HUP signals and/or make use of restarts.
439 * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
442 * TODO: make conditional?
444 static const struct sock_filter filter_instr[] = {
446 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
447 /* L5, L1, is UDP? */
448 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0),
449 /* ugly check for arp on ethernet-like and IPv4 */
450 BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */
451 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4),/* L3, L4 */
453 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */
454 /* check udp source and destination ports */
455 BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
457 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),
459 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* L3: pass */
460 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
463 static const struct sock_fprog filter_prog = {
464 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
465 /* casting const away: */
466 .filter = (struct sock_filter *) filter_instr,
469 fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
473 if (SERVER_PORT == 67 && CLIENT_PORT == 68)
474 /* Use only if standard ports are in use */
475 setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
476 sizeof(filter_prog));
478 sock.sll_family = AF_PACKET;
479 sock.sll_protocol = htons(ETH_P_IP);
480 sock.sll_ifindex = ifindex;
482 if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
490 static gboolean sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
492 if (packet->ip.protocol != IPPROTO_UDP)
495 if (packet->ip.version != IPVERSION)
498 if (packet->ip.ihl != sizeof(packet->ip) >> 2)
501 if (packet->udp.dest != htons(CLIENT_PORT))
504 if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip)))
510 static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
513 struct ip_udp_dhcp_packet packet;
516 memset(&packet, 0, sizeof(packet));
518 bytes = read(fd, &packet, sizeof(packet));
522 if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
525 if (bytes < ntohs(packet.ip.tot_len))
526 /* packet is bigger than sizeof(packet), we did partial read */
529 /* ignore any extra garbage bytes */
530 bytes = ntohs(packet.ip.tot_len);
532 if (sanity_check(&packet, bytes) == FALSE)
535 check = packet.ip.check;
537 if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip)))
540 /* verify UDP checksum. IP header has to be modified for this */
541 memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
542 /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
543 packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
544 check = packet.udp.check;
545 packet.udp.check = 0;
546 if (check && check != dhcp_checksum(&packet, bytes))
549 memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) +
550 sizeof(packet.udp)));
552 if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
555 return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
558 static gboolean check_package_owner(GDHCPClient *dhcp_client,
559 struct dhcp_packet *packet)
561 if (packet->xid != dhcp_client->xid)
564 if (packet->hlen != 6)
567 if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
573 static void start_request(GDHCPClient *dhcp_client);
575 static gboolean request_timeout(gpointer user_data)
577 GDHCPClient *dhcp_client = user_data;
579 dhcp_client->retry_times++;
581 start_request(dhcp_client);
586 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
589 static int switch_listening_mode(GDHCPClient *dhcp_client,
590 ListenMode listen_mode)
592 GIOChannel *listener_channel;
595 if (dhcp_client->listen_mode == listen_mode)
598 if (dhcp_client->listen_mode != L_NONE) {
599 g_source_remove(dhcp_client->listener_watch);
600 dhcp_client->listener_channel = NULL;
601 dhcp_client->listen_mode = L_NONE;
602 dhcp_client->listener_sockfd = -1;
603 dhcp_client->listener_watch = 0;
606 if (listen_mode == L_NONE)
609 if (listen_mode == L2)
610 listener_sockfd = dhcp_l2_socket(dhcp_client->ifindex);
611 else if (listen_mode == L3)
612 listener_sockfd = dhcp_l3_socket(CLIENT_PORT,
613 dhcp_client->interface);
617 if (listener_sockfd < 0)
620 listener_channel = g_io_channel_unix_new(listener_sockfd);
621 if (listener_channel == NULL) {
622 /* Failed to create listener channel */
623 close(listener_sockfd);
627 dhcp_client->listen_mode = listen_mode;
628 dhcp_client->listener_sockfd = listener_sockfd;
629 dhcp_client->listener_channel = listener_channel;
631 g_io_channel_set_close_on_unref(listener_channel, TRUE);
632 dhcp_client->listener_watch =
633 g_io_add_watch_full(listener_channel,
634 G_PRIORITY_HIGH, G_IO_IN,
635 listener_event, dhcp_client,
637 g_io_channel_unref(dhcp_client->listener_channel);
642 static void start_request(GDHCPClient *dhcp_client)
644 if (dhcp_client->retry_times == REQUEST_RETRIES) {
645 dhcp_client->state = INIT_SELECTING;
647 if (dhcp_client->no_lease_cb != NULL)
648 dhcp_client->no_lease_cb(dhcp_client,
649 dhcp_client->no_lease_data);
654 if (dhcp_client->retry_times == 0) {
655 dhcp_client->state = REQUESTING;
656 switch_listening_mode(dhcp_client, L2);
659 send_select(dhcp_client);
661 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
668 static uint32_t get_lease(struct dhcp_packet *packet)
671 uint32_t lease_seconds;
673 option_u8 = dhcp_get_option(packet, DHCP_LEASE_TIME);
674 if (option_u8 == NULL)
677 lease_seconds = dhcp_get_unaligned((uint32_t *) option_u8);
678 lease_seconds = ntohl(lease_seconds);
679 /* paranoia: must not be prone to overflows */
680 lease_seconds &= 0x0fffffff;
681 if (lease_seconds < 10)
684 return lease_seconds;
687 static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times)
689 if (dhcp_client->timeout > 0) {
690 g_source_remove(dhcp_client->timeout);
691 dhcp_client->timeout = 0;
694 dhcp_client->retry_times = retry_times;
695 dhcp_client->requested_ip = 0;
696 switch_listening_mode(dhcp_client, L2);
698 g_dhcp_client_start(dhcp_client);
701 static gboolean start_rebound_timeout(gpointer user_data)
703 GDHCPClient *dhcp_client = user_data;
705 switch_listening_mode(dhcp_client, L2);
707 dhcp_client->lease_seconds >>= 1;
709 /* We need to have enough time to receive ACK package*/
710 if (dhcp_client->lease_seconds <= 6) {
712 /* ip need to be cleared */
713 if (dhcp_client->lease_lost_cb != NULL)
714 dhcp_client->lease_lost_cb(dhcp_client,
715 dhcp_client->lease_lost_data);
717 restart_dhcp(dhcp_client, 0);
719 send_rebound(dhcp_client);
721 dhcp_client->timeout =
722 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
723 dhcp_client->lease_seconds >> 1,
724 start_rebound_timeout,
732 static void start_rebound(GDHCPClient *dhcp_client)
734 dhcp_client->state = REBINDING;
736 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
737 dhcp_client->lease_seconds >> 1,
738 start_rebound_timeout,
743 static gboolean start_renew_timeout(gpointer user_data)
745 GDHCPClient *dhcp_client = user_data;
749 elapse = g_timer_elapsed(timer, µseconds);
751 g_timer_start(timer);
753 dhcp_client->state = RENEWING;
755 dhcp_client->lease_seconds >>= 1;
757 switch_listening_mode(dhcp_client, L3);
758 if (dhcp_client->lease_seconds <= 60)
759 start_rebound(dhcp_client);
761 send_renew(dhcp_client);
763 dhcp_client->timeout =
764 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
765 dhcp_client->lease_seconds >> 1,
774 static void start_bound(GDHCPClient *dhcp_client)
776 dhcp_client->state = BOUND;
779 timer = g_timer_new();
781 dhcp_client->timeout =
782 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
783 dhcp_client->lease_seconds >> 1,
784 start_renew_timeout, dhcp_client,
788 static gboolean restart_dhcp_timeout(gpointer user_data)
790 GDHCPClient *dhcp_client = user_data;
792 dhcp_client->ack_retry_times++;
794 restart_dhcp(dhcp_client, dhcp_client->ack_retry_times);
799 static char *get_ip(uint32_t ip)
805 return g_strdup(inet_ntoa(addr));
808 /* get a rough idea of how long an option will be */
809 static const uint8_t len_of_option_as_string[] = {
810 [OPTION_IP] = sizeof("255.255.255.255 "),
812 [OPTION_U8] = sizeof("255 "),
813 [OPTION_U16] = sizeof("65535 "),
814 [OPTION_U32] = sizeof("4294967295 "),
817 static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
819 return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
822 /* Create "opt_value1 option_value2 ..." string */
823 static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
825 unsigned upper_length;
829 len = option[OPT_LEN - OPT_DATA];
830 type &= OPTION_TYPE_MASK;
831 optlen = dhcp_option_lengths[type];
834 upper_length = len_of_option_as_string[type] *
835 ((unsigned)len / (unsigned)optlen);
836 dest = ret = malloc(upper_length + 1);
840 while (len >= optlen) {
843 dest += sprint_nip(dest, "", option);
846 uint16_t val_u16 = dhcp_get_unaligned(
847 (uint16_t *) option);
848 dest += sprintf(dest, "%u", ntohs(val_u16));
852 uint32_t val_u32 = dhcp_get_unaligned(
853 (uint32_t *) option);
854 dest += sprintf(dest, type == OPTION_U32 ? "%lu" :
855 "%ld", (unsigned long) ntohl(val_u32));
859 memcpy(dest, option, len);
876 static GList *get_option_value_list(char *value)
881 while ((pos = strchr(pos, ' ')) != NULL) {
884 list = g_list_append(list, g_strdup(value));
889 list = g_list_append(list, g_strdup(value));
894 static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
896 GDHCPOptionType type;
897 GList *list, *value_list;
902 for (list = dhcp_client->request_list; list; list = list->next) {
903 code = (uint8_t) GPOINTER_TO_INT(list->data);
905 option = dhcp_get_option(packet, code);
906 if (option == NULL) {
907 g_hash_table_remove(dhcp_client->code_value_hash,
908 GINT_TO_POINTER((int) code));
912 type = dhcp_get_code_type(code);
914 option_value = malloc_option_value_string(option, type);
915 if (option_value == NULL)
916 g_hash_table_remove(dhcp_client->code_value_hash,
917 GINT_TO_POINTER((int) code));
919 value_list = get_option_value_list(option_value);
921 g_free(option_value);
923 if (value_list == NULL)
924 g_hash_table_remove(dhcp_client->code_value_hash,
925 GINT_TO_POINTER((int) code));
927 g_hash_table_insert(dhcp_client->code_value_hash,
928 GINT_TO_POINTER((int) code), value_list);
932 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
935 GDHCPClient *dhcp_client = user_data;
936 struct dhcp_packet packet;
937 uint8_t *message_type, *option_u8;
940 if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
941 dhcp_client->listener_watch = 0;
945 if (dhcp_client->listen_mode == L_NONE)
948 if (dhcp_client->listen_mode == L2)
949 re = dhcp_recv_l2_packet(&packet, dhcp_client->listener_sockfd);
950 else if (dhcp_client->listen_mode == L3)
951 re = dhcp_recv_l3_packet(&packet, dhcp_client->listener_sockfd);
958 if (check_package_owner(dhcp_client, &packet) == FALSE)
961 message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
962 if (message_type == NULL)
963 /* No message type option, ignore pakcage */
966 switch (dhcp_client->state) {
968 if (*message_type != DHCPOFFER)
971 g_source_remove(dhcp_client->timeout);
972 dhcp_client->timeout = 0;
973 dhcp_client->retry_times = 0;
975 option_u8 = dhcp_get_option(&packet, DHCP_SERVER_ID);
976 dhcp_client->server_ip =
977 dhcp_get_unaligned((uint32_t *) option_u8);
978 dhcp_client->requested_ip = packet.yiaddr;
980 dhcp_client->state = REQUESTING;
982 start_request(dhcp_client);
988 if (*message_type == DHCPACK) {
989 dhcp_client->retry_times = 0;
991 if (dhcp_client->timeout > 0)
992 g_source_remove(dhcp_client->timeout);
993 dhcp_client->timeout = 0;
995 dhcp_client->lease_seconds = get_lease(&packet);
997 get_request(dhcp_client, &packet);
999 switch_listening_mode(dhcp_client, L_NONE);
1001 g_free(dhcp_client->assigned_ip);
1002 dhcp_client->assigned_ip = get_ip(packet.yiaddr);
1004 /* Address should be set up here */
1005 if (dhcp_client->lease_available_cb != NULL)
1006 dhcp_client->lease_available_cb(dhcp_client,
1007 dhcp_client->lease_available_data);
1009 start_bound(dhcp_client);
1010 } else if (*message_type == DHCPNAK) {
1011 dhcp_client->retry_times = 0;
1013 if (dhcp_client->timeout > 0)
1014 g_source_remove(dhcp_client->timeout);
1016 dhcp_client->timeout = g_timeout_add_seconds_full(
1018 restart_dhcp_timeout,
1031 static gboolean discover_timeout(gpointer user_data)
1033 GDHCPClient *dhcp_client = user_data;
1035 dhcp_client->retry_times++;
1037 g_dhcp_client_start(dhcp_client);
1042 int g_dhcp_client_start(GDHCPClient *dhcp_client)
1046 if (dhcp_client->retry_times == DISCOVER_RETRIES) {
1047 if (dhcp_client->no_lease_cb != NULL)
1048 dhcp_client->no_lease_cb(dhcp_client,
1049 dhcp_client->no_lease_data);
1054 if (dhcp_client->retry_times == 0) {
1055 g_free(dhcp_client->assigned_ip);
1056 dhcp_client->assigned_ip = NULL;
1058 dhcp_client->state = INIT_SELECTING;
1059 re = switch_listening_mode(dhcp_client, L2);
1063 dhcp_client->xid = rand();
1066 send_discover(dhcp_client, 0);
1068 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1076 void g_dhcp_client_stop(GDHCPClient *dhcp_client)
1078 switch_listening_mode(dhcp_client, L_NONE);
1080 if (dhcp_client->state == BOUND ||
1081 dhcp_client->state == RENEWING ||
1082 dhcp_client->state == REBINDING)
1083 send_release(dhcp_client, dhcp_client->server_ip,
1084 dhcp_client->requested_ip);
1086 if (dhcp_client->timeout > 0) {
1087 g_source_remove(dhcp_client->timeout);
1088 dhcp_client->timeout = 0;
1091 if (dhcp_client->listener_watch > 0) {
1092 g_source_remove(dhcp_client->listener_watch);
1093 dhcp_client->listener_watch = 0;
1096 dhcp_client->listener_channel = NULL;
1098 dhcp_client->retry_times = 0;
1099 dhcp_client->ack_retry_times = 0;
1101 dhcp_client->requested_ip = 0;
1102 dhcp_client->state = RELEASED;
1103 dhcp_client->lease_seconds = 0;
1106 GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
1107 unsigned char option_code)
1109 return g_hash_table_lookup(dhcp_client->code_value_hash,
1110 GINT_TO_POINTER((int) option_code));
1113 void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
1114 GDHCPClientEvent event,
1115 GDHCPClientEventFunc func,
1119 case G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE:
1120 dhcp_client->lease_available_cb = func;
1121 dhcp_client->lease_available_data = data;
1123 case G_DHCP_CLIENT_EVENT_NO_LEASE:
1124 dhcp_client->no_lease_cb = func;
1125 dhcp_client->no_lease_data = data;
1127 case G_DHCP_CLIENT_EVENT_LEASE_LOST:
1128 dhcp_client->lease_lost_cb = func;
1129 dhcp_client->lease_lost_data = data;
1131 case G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT:
1132 dhcp_client->address_conflict_cb = func;
1133 dhcp_client->address_conflict_data = data;
1138 int g_dhcp_client_get_index(GDHCPClient *dhcp_client)
1140 return dhcp_client->ifindex;
1143 char *g_dhcp_client_get_address(GDHCPClient *dhcp_client)
1145 return g_strdup(dhcp_client->assigned_ip);
1148 GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
1149 unsigned char option_code)
1151 if (g_list_find(dhcp_client->request_list,
1152 GINT_TO_POINTER((int) option_code)) == NULL)
1153 dhcp_client->request_list = g_list_prepend(
1154 dhcp_client->request_list,
1155 (GINT_TO_POINTER((int) option_code)));
1157 return G_DHCP_CLIENT_ERROR_NONE;
1160 static uint8_t *alloc_dhcp_option(int code, const char *str, int extra)
1163 int len = strnlen(str, 255);
1165 storage = malloc(len + extra + OPT_DATA);
1166 storage[OPT_CODE] = code;
1167 storage[OPT_LEN] = len + extra;
1168 memcpy(storage + extra + OPT_DATA, str, len);
1173 static const char *get_hostname(const char *host)
1175 char local_host_name[HOST_NAME_MAX + 1];
1177 if (g_strcmp0("<hostname>", host) != 0)
1178 return g_strdup(host);
1180 if (gethostname(local_host_name, HOST_NAME_MAX) != 0)
1183 local_host_name[HOST_NAME_MAX] = 0;
1185 return g_strdup(local_host_name);
1188 /* Now only support send hostname */
1189 GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
1190 unsigned char option_code, const char *option_value)
1192 uint8_t *binary_option;
1193 const char *hostname;
1195 if (option_code == G_DHCP_HOST_NAME) {
1196 hostname = get_hostname(option_value);
1198 binary_option = alloc_dhcp_option(option_code, hostname, 0);
1200 g_hash_table_insert(dhcp_client->send_value_hash,
1201 GINT_TO_POINTER((int) option_code), binary_option);
1204 return G_DHCP_CLIENT_ERROR_NONE;
1207 void g_dhcp_client_ref(GDHCPClient *dhcp_client)
1209 g_atomic_int_inc(&dhcp_client->ref_count);
1212 void g_dhcp_client_unref(GDHCPClient *dhcp_client)
1214 if (g_atomic_int_dec_and_test(&dhcp_client->ref_count) == FALSE)
1217 g_dhcp_client_stop(dhcp_client);
1219 g_free(dhcp_client->interface);
1220 g_free(dhcp_client->assigned_ip);
1222 g_list_free(dhcp_client->request_list);
1223 g_list_free(dhcp_client->require_list);
1225 g_hash_table_destroy(dhcp_client->code_value_hash);
1226 g_hash_table_destroy(dhcp_client->send_value_hash);
1228 g_free(dhcp_client);
1231 void g_dhcp_client_set_debug(GDHCPClient *dhcp_client,
1232 GDHCPDebugFunc func, gpointer data)
1234 dhcp_client->debug_func = func;
1235 dhcp_client->debug_data = data;