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 static inline void debug(GDHCPClient *client, const char *format, ...)
109 if (client->debug_func == NULL)
112 va_start(ap, format);
114 if (vsnprintf(str, sizeof(str), format, ap) > 0)
115 client->debug_func(str, client->debug_data);
120 /* Initialize the packet with the proper defaults */
121 static void init_packet(GDHCPClient *dhcp_client,
122 struct dhcp_packet *packet, char type)
124 dhcp_init_header(packet, type);
126 memcpy(packet->chaddr, dhcp_client->mac_address, 6);
129 static void add_request_options(GDHCPClient *dhcp_client,
130 struct dhcp_packet *packet)
135 int end = dhcp_end_option(packet->options);
137 for (list = dhcp_client->request_list; list; list = list->next) {
138 code = (uint8_t) GPOINTER_TO_INT(list->data);
140 packet->options[end + OPT_DATA + len] = code;
145 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
146 packet->options[end + OPT_LEN] = len;
147 packet->options[end + OPT_DATA + len] = DHCP_END;
151 static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
153 uint8_t *option = value;
154 struct dhcp_packet *packet = user_data;
156 dhcp_add_binary_option(packet, option);
159 static void add_send_options(GDHCPClient *dhcp_client,
160 struct dhcp_packet *packet)
162 g_hash_table_foreach(dhcp_client->send_value_hash,
163 add_binary_option, packet);
166 static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
168 struct dhcp_packet packet;
170 debug(dhcp_client, "sending DHCP discover request");
172 init_packet(dhcp_client, &packet, DHCPDISCOVER);
174 packet.xid = dhcp_client->xid;
177 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
179 /* Explicitly saying that we want RFC-compliant packets helps
180 * some buggy DHCP servers to NOT send bigger packets */
181 dhcp_add_simple_option(&packet, DHCP_MAX_SIZE, htons(576));
183 add_request_options(dhcp_client, &packet);
185 add_send_options(dhcp_client, &packet);
187 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
188 INADDR_BROADCAST, SERVER_PORT,
189 MAC_BCAST_ADDR, dhcp_client->ifindex);
192 static int send_select(GDHCPClient *dhcp_client)
194 struct dhcp_packet packet;
197 debug(dhcp_client, "sending DHCP select request");
199 init_packet(dhcp_client, &packet, DHCPREQUEST);
201 packet.xid = dhcp_client->xid;
203 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP,
204 dhcp_client->requested_ip);
205 dhcp_add_simple_option(&packet, DHCP_SERVER_ID, dhcp_client->server_ip);
207 add_request_options(dhcp_client, &packet);
209 add_send_options(dhcp_client, &packet);
211 addr.s_addr = dhcp_client->requested_ip;
213 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
214 INADDR_BROADCAST, SERVER_PORT,
215 MAC_BCAST_ADDR, dhcp_client->ifindex);
218 static int send_renew(GDHCPClient *dhcp_client)
220 struct dhcp_packet packet;
222 debug(dhcp_client, "sending DHCP renew request");
224 init_packet(dhcp_client , &packet, DHCPREQUEST);
225 packet.xid = dhcp_client->xid;
226 packet.ciaddr = dhcp_client->requested_ip;
228 add_request_options(dhcp_client, &packet);
230 add_send_options(dhcp_client, &packet);
232 return dhcp_send_kernel_packet(&packet,
233 dhcp_client->requested_ip, CLIENT_PORT,
234 dhcp_client->server_ip, SERVER_PORT);
237 static int send_rebound(GDHCPClient *dhcp_client)
239 struct dhcp_packet packet;
241 debug(dhcp_client, "sending DHCP rebound request");
243 init_packet(dhcp_client , &packet, DHCPREQUEST);
244 packet.xid = dhcp_client->xid;
245 packet.ciaddr = dhcp_client->requested_ip;
247 add_request_options(dhcp_client, &packet);
249 add_send_options(dhcp_client, &packet);
251 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
252 INADDR_BROADCAST, SERVER_PORT,
253 MAC_BCAST_ADDR, dhcp_client->ifindex);
256 static int send_release(GDHCPClient *dhcp_client,
257 uint32_t server, uint32_t ciaddr)
259 struct dhcp_packet packet;
261 debug(dhcp_client, "sending DHCP release request");
263 init_packet(dhcp_client, &packet, DHCPRELEASE);
265 packet.ciaddr = ciaddr;
267 dhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
269 return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
270 server, SERVER_PORT);
273 static gboolean interface_is_up(int index)
277 gboolean ret = FALSE;
279 sk = socket(PF_INET, SOCK_DGRAM, 0);
281 perror("Open socket error");
285 memset(&ifr, 0, sizeof(ifr));
286 ifr.ifr_ifindex = index;
288 err = ioctl(sk, SIOCGIFNAME, &ifr);
290 perror("Get interface name error");
294 err = ioctl(sk, SIOCGIFFLAGS, &ifr);
296 perror("Get interface flags error");
300 if (ifr.ifr_flags & IFF_UP)
309 static char *get_interface_name(int index)
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");
335 return g_strdup(ifr.ifr_name);
338 static void get_interface_mac_address(int index, uint8_t *mac_address)
343 sk = socket(PF_INET, SOCK_DGRAM, 0);
345 perror("Open socket error");
349 memset(&ifr, 0, sizeof(ifr));
350 ifr.ifr_ifindex = index;
352 err = ioctl(sk, SIOCGIFNAME, &ifr);
354 perror("Get interface name error");
358 err = ioctl(sk, SIOCGIFHWADDR, &ifr);
360 perror("Get mac address error");
364 memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
370 static void remove_value(gpointer data, gpointer user_data)
376 static void remove_option_value(gpointer data)
378 GList *option_value = data;
380 g_list_foreach(option_value, remove_value, NULL);
383 GDHCPClient *g_dhcp_client_new(GDHCPType type,
384 int ifindex, GDHCPClientError *error)
386 GDHCPClient *dhcp_client;
389 *error = G_DHCP_CLIENT_ERROR_INVALID_INDEX;
393 dhcp_client = g_try_new0(GDHCPClient, 1);
394 if (dhcp_client == NULL) {
395 *error = G_DHCP_CLIENT_ERROR_NOMEM;
399 dhcp_client->interface = get_interface_name(ifindex);
400 if (dhcp_client->interface == NULL) {
401 *error = G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE;
405 if (interface_is_up(ifindex) == FALSE) {
406 *error = G_DHCP_CLIENT_ERROR_INTERFACE_DOWN;
410 get_interface_mac_address(ifindex, dhcp_client->mac_address);
412 dhcp_client->listener_sockfd = -1;
413 dhcp_client->listener_channel = NULL;
414 dhcp_client->listen_mode = L_NONE;
415 dhcp_client->ref_count = 1;
416 dhcp_client->type = type;
417 dhcp_client->ifindex = ifindex;
418 dhcp_client->lease_available_cb = NULL;
419 dhcp_client->no_lease_cb = NULL;
420 dhcp_client->lease_lost_cb = NULL;
421 dhcp_client->address_conflict_cb = NULL;
422 dhcp_client->listener_watch = 0;
423 dhcp_client->retry_times = 0;
424 dhcp_client->ack_retry_times = 0;
425 dhcp_client->code_value_hash = g_hash_table_new_full(g_direct_hash,
426 g_direct_equal, NULL, remove_option_value);
427 dhcp_client->send_value_hash = g_hash_table_new_full(g_direct_hash,
428 g_direct_equal, NULL, g_free);
429 dhcp_client->request_list = NULL;
430 dhcp_client->require_list = NULL;
432 *error = G_DHCP_CLIENT_ERROR_NONE;
437 g_free(dhcp_client->interface);
442 #define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68)
444 static int dhcp_l2_socket(int ifindex)
447 struct sockaddr_ll sock;
452 * I've selected not to see LL header, so BPF doesn't see it, too.
453 * The filter may also pass non-IP and non-ARP packets, but we do
454 * a more complete check when receiving the message in userspace.
456 * and filter shamelessly stolen from:
458 * http://www.flamewarmaster.de/software/dhcpclient/
460 * There are a few other interesting ideas on that page (look under
461 * "Motivation"). Use of netlink events is most interesting. Think
462 * of various network servers listening for events and reconfiguring.
463 * That would obsolete sending HUP signals and/or make use of restarts.
465 * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
468 * TODO: make conditional?
470 static const struct sock_filter filter_instr[] = {
472 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
473 /* L5, L1, is UDP? */
474 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0),
475 /* ugly check for arp on ethernet-like and IPv4 */
476 BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */
477 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4),/* L3, L4 */
479 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */
480 /* check udp source and destination ports */
481 BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
483 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),
485 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* L3: pass */
486 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
489 static const struct sock_fprog filter_prog = {
490 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
491 /* casting const away: */
492 .filter = (struct sock_filter *) filter_instr,
495 fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
499 if (SERVER_PORT == 67 && CLIENT_PORT == 68)
500 /* Use only if standard ports are in use */
501 setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
502 sizeof(filter_prog));
504 sock.sll_family = AF_PACKET;
505 sock.sll_protocol = htons(ETH_P_IP);
506 sock.sll_ifindex = ifindex;
508 if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
516 static gboolean sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
518 if (packet->ip.protocol != IPPROTO_UDP)
521 if (packet->ip.version != IPVERSION)
524 if (packet->ip.ihl != sizeof(packet->ip) >> 2)
527 if (packet->udp.dest != htons(CLIENT_PORT))
530 if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip)))
536 static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
539 struct ip_udp_dhcp_packet packet;
542 memset(&packet, 0, sizeof(packet));
544 bytes = read(fd, &packet, sizeof(packet));
548 if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
551 if (bytes < ntohs(packet.ip.tot_len))
552 /* packet is bigger than sizeof(packet), we did partial read */
555 /* ignore any extra garbage bytes */
556 bytes = ntohs(packet.ip.tot_len);
558 if (sanity_check(&packet, bytes) == FALSE)
561 check = packet.ip.check;
563 if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip)))
566 /* verify UDP checksum. IP header has to be modified for this */
567 memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
568 /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
569 packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
570 check = packet.udp.check;
571 packet.udp.check = 0;
572 if (check && check != dhcp_checksum(&packet, bytes))
575 memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) +
576 sizeof(packet.udp)));
578 if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
581 return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
584 static gboolean check_package_owner(GDHCPClient *dhcp_client,
585 struct dhcp_packet *packet)
587 if (packet->xid != dhcp_client->xid)
590 if (packet->hlen != 6)
593 if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
599 static void start_request(GDHCPClient *dhcp_client);
601 static gboolean request_timeout(gpointer user_data)
603 GDHCPClient *dhcp_client = user_data;
605 dhcp_client->retry_times++;
607 start_request(dhcp_client);
612 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
615 static int switch_listening_mode(GDHCPClient *dhcp_client,
616 ListenMode listen_mode)
618 GIOChannel *listener_channel;
621 if (dhcp_client->listen_mode == listen_mode)
624 if (dhcp_client->listen_mode != L_NONE) {
625 g_source_remove(dhcp_client->listener_watch);
626 dhcp_client->listener_channel = NULL;
627 dhcp_client->listen_mode = L_NONE;
628 dhcp_client->listener_sockfd = -1;
629 dhcp_client->listener_watch = 0;
632 if (listen_mode == L_NONE)
635 if (listen_mode == L2)
636 listener_sockfd = dhcp_l2_socket(dhcp_client->ifindex);
637 else if (listen_mode == L3)
638 listener_sockfd = dhcp_l3_socket(CLIENT_PORT,
639 dhcp_client->interface);
643 if (listener_sockfd < 0)
646 listener_channel = g_io_channel_unix_new(listener_sockfd);
647 if (listener_channel == NULL) {
648 /* Failed to create listener channel */
649 close(listener_sockfd);
653 dhcp_client->listen_mode = listen_mode;
654 dhcp_client->listener_sockfd = listener_sockfd;
655 dhcp_client->listener_channel = listener_channel;
657 g_io_channel_set_close_on_unref(listener_channel, TRUE);
658 dhcp_client->listener_watch =
659 g_io_add_watch_full(listener_channel,
660 G_PRIORITY_HIGH, G_IO_IN,
661 listener_event, dhcp_client,
663 g_io_channel_unref(dhcp_client->listener_channel);
668 static void start_request(GDHCPClient *dhcp_client)
670 if (dhcp_client->retry_times == REQUEST_RETRIES) {
671 dhcp_client->state = INIT_SELECTING;
673 if (dhcp_client->no_lease_cb != NULL)
674 dhcp_client->no_lease_cb(dhcp_client,
675 dhcp_client->no_lease_data);
680 if (dhcp_client->retry_times == 0) {
681 dhcp_client->state = REQUESTING;
682 switch_listening_mode(dhcp_client, L2);
685 send_select(dhcp_client);
687 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
694 static uint32_t get_lease(struct dhcp_packet *packet)
697 uint32_t lease_seconds;
699 option_u8 = dhcp_get_option(packet, DHCP_LEASE_TIME);
700 if (option_u8 == NULL)
703 lease_seconds = dhcp_get_unaligned((uint32_t *) option_u8);
704 lease_seconds = ntohl(lease_seconds);
705 /* paranoia: must not be prone to overflows */
706 lease_seconds &= 0x0fffffff;
707 if (lease_seconds < 10)
710 return lease_seconds;
713 static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times)
715 if (dhcp_client->timeout > 0) {
716 g_source_remove(dhcp_client->timeout);
717 dhcp_client->timeout = 0;
720 dhcp_client->retry_times = retry_times;
721 dhcp_client->requested_ip = 0;
722 switch_listening_mode(dhcp_client, L2);
724 g_dhcp_client_start(dhcp_client);
727 static gboolean start_rebound_timeout(gpointer user_data)
729 GDHCPClient *dhcp_client = user_data;
731 switch_listening_mode(dhcp_client, L2);
733 dhcp_client->lease_seconds >>= 1;
735 /* We need to have enough time to receive ACK package*/
736 if (dhcp_client->lease_seconds <= 6) {
738 /* ip need to be cleared */
739 if (dhcp_client->lease_lost_cb != NULL)
740 dhcp_client->lease_lost_cb(dhcp_client,
741 dhcp_client->lease_lost_data);
743 restart_dhcp(dhcp_client, 0);
745 send_rebound(dhcp_client);
747 dhcp_client->timeout =
748 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
749 dhcp_client->lease_seconds >> 1,
750 start_rebound_timeout,
758 static void start_rebound(GDHCPClient *dhcp_client)
760 dhcp_client->state = REBINDING;
762 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
763 dhcp_client->lease_seconds >> 1,
764 start_rebound_timeout,
769 static gboolean start_renew_timeout(gpointer user_data)
771 GDHCPClient *dhcp_client = user_data;
775 elapse = g_timer_elapsed(timer, µseconds);
777 g_timer_start(timer);
779 dhcp_client->state = RENEWING;
781 dhcp_client->lease_seconds >>= 1;
783 switch_listening_mode(dhcp_client, L3);
784 if (dhcp_client->lease_seconds <= 60)
785 start_rebound(dhcp_client);
787 send_renew(dhcp_client);
789 dhcp_client->timeout =
790 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
791 dhcp_client->lease_seconds >> 1,
800 static void start_bound(GDHCPClient *dhcp_client)
802 dhcp_client->state = BOUND;
805 timer = g_timer_new();
807 dhcp_client->timeout =
808 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
809 dhcp_client->lease_seconds >> 1,
810 start_renew_timeout, dhcp_client,
814 static gboolean restart_dhcp_timeout(gpointer user_data)
816 GDHCPClient *dhcp_client = user_data;
818 dhcp_client->ack_retry_times++;
820 restart_dhcp(dhcp_client, dhcp_client->ack_retry_times);
825 static char *get_ip(uint32_t ip)
831 return g_strdup(inet_ntoa(addr));
834 /* get a rough idea of how long an option will be */
835 static const uint8_t len_of_option_as_string[] = {
836 [OPTION_IP] = sizeof("255.255.255.255 "),
838 [OPTION_U8] = sizeof("255 "),
839 [OPTION_U16] = sizeof("65535 "),
840 [OPTION_U32] = sizeof("4294967295 "),
843 static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
845 return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
848 /* Create "opt_value1 option_value2 ..." string */
849 static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
851 unsigned upper_length;
855 len = option[OPT_LEN - OPT_DATA];
856 type &= OPTION_TYPE_MASK;
857 optlen = dhcp_option_lengths[type];
860 upper_length = len_of_option_as_string[type] *
861 ((unsigned)len / (unsigned)optlen);
862 dest = ret = malloc(upper_length + 1);
866 while (len >= optlen) {
869 dest += sprint_nip(dest, "", option);
872 uint16_t val_u16 = dhcp_get_unaligned(
873 (uint16_t *) option);
874 dest += sprintf(dest, "%u", ntohs(val_u16));
878 uint32_t val_u32 = dhcp_get_unaligned(
879 (uint32_t *) option);
880 dest += sprintf(dest, type == OPTION_U32 ? "%lu" :
881 "%ld", (unsigned long) ntohl(val_u32));
885 memcpy(dest, option, len);
902 static GList *get_option_value_list(char *value)
907 while ((pos = strchr(pos, ' ')) != NULL) {
910 list = g_list_append(list, g_strdup(value));
915 list = g_list_append(list, g_strdup(value));
920 static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
922 GDHCPOptionType type;
923 GList *list, *value_list;
928 for (list = dhcp_client->request_list; list; list = list->next) {
929 code = (uint8_t) GPOINTER_TO_INT(list->data);
931 option = dhcp_get_option(packet, code);
932 if (option == NULL) {
933 g_hash_table_remove(dhcp_client->code_value_hash,
934 GINT_TO_POINTER((int) code));
938 type = dhcp_get_code_type(code);
940 option_value = malloc_option_value_string(option, type);
941 if (option_value == NULL)
942 g_hash_table_remove(dhcp_client->code_value_hash,
943 GINT_TO_POINTER((int) code));
945 value_list = get_option_value_list(option_value);
947 g_free(option_value);
949 if (value_list == NULL)
950 g_hash_table_remove(dhcp_client->code_value_hash,
951 GINT_TO_POINTER((int) code));
953 g_hash_table_insert(dhcp_client->code_value_hash,
954 GINT_TO_POINTER((int) code), value_list);
958 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
961 GDHCPClient *dhcp_client = user_data;
962 struct dhcp_packet packet;
963 uint8_t *message_type, *option_u8;
966 if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
967 dhcp_client->listener_watch = 0;
971 if (dhcp_client->listen_mode == L_NONE)
974 if (dhcp_client->listen_mode == L2)
975 re = dhcp_recv_l2_packet(&packet, dhcp_client->listener_sockfd);
976 else if (dhcp_client->listen_mode == L3)
977 re = dhcp_recv_l3_packet(&packet, dhcp_client->listener_sockfd);
984 if (check_package_owner(dhcp_client, &packet) == FALSE)
987 message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
988 if (message_type == NULL)
989 /* No message type option, ignore pakcage */
992 switch (dhcp_client->state) {
994 if (*message_type != DHCPOFFER)
997 g_source_remove(dhcp_client->timeout);
998 dhcp_client->timeout = 0;
999 dhcp_client->retry_times = 0;
1001 option_u8 = dhcp_get_option(&packet, DHCP_SERVER_ID);
1002 dhcp_client->server_ip =
1003 dhcp_get_unaligned((uint32_t *) option_u8);
1004 dhcp_client->requested_ip = packet.yiaddr;
1006 dhcp_client->state = REQUESTING;
1008 start_request(dhcp_client);
1014 if (*message_type == DHCPACK) {
1015 dhcp_client->retry_times = 0;
1017 if (dhcp_client->timeout > 0)
1018 g_source_remove(dhcp_client->timeout);
1019 dhcp_client->timeout = 0;
1021 dhcp_client->lease_seconds = get_lease(&packet);
1023 get_request(dhcp_client, &packet);
1025 switch_listening_mode(dhcp_client, L_NONE);
1027 g_free(dhcp_client->assigned_ip);
1028 dhcp_client->assigned_ip = get_ip(packet.yiaddr);
1030 /* Address should be set up here */
1031 if (dhcp_client->lease_available_cb != NULL)
1032 dhcp_client->lease_available_cb(dhcp_client,
1033 dhcp_client->lease_available_data);
1035 start_bound(dhcp_client);
1036 } else if (*message_type == DHCPNAK) {
1037 dhcp_client->retry_times = 0;
1039 if (dhcp_client->timeout > 0)
1040 g_source_remove(dhcp_client->timeout);
1042 dhcp_client->timeout = g_timeout_add_seconds_full(
1044 restart_dhcp_timeout,
1057 static gboolean discover_timeout(gpointer user_data)
1059 GDHCPClient *dhcp_client = user_data;
1061 dhcp_client->retry_times++;
1063 g_dhcp_client_start(dhcp_client);
1068 int g_dhcp_client_start(GDHCPClient *dhcp_client)
1072 if (dhcp_client->retry_times == DISCOVER_RETRIES) {
1073 if (dhcp_client->no_lease_cb != NULL)
1074 dhcp_client->no_lease_cb(dhcp_client,
1075 dhcp_client->no_lease_data);
1080 if (dhcp_client->retry_times == 0) {
1081 g_free(dhcp_client->assigned_ip);
1082 dhcp_client->assigned_ip = NULL;
1084 dhcp_client->state = INIT_SELECTING;
1085 re = switch_listening_mode(dhcp_client, L2);
1089 dhcp_client->xid = rand();
1092 send_discover(dhcp_client, 0);
1094 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1102 void g_dhcp_client_stop(GDHCPClient *dhcp_client)
1104 switch_listening_mode(dhcp_client, L_NONE);
1106 if (dhcp_client->state == BOUND ||
1107 dhcp_client->state == RENEWING ||
1108 dhcp_client->state == REBINDING)
1109 send_release(dhcp_client, dhcp_client->server_ip,
1110 dhcp_client->requested_ip);
1112 if (dhcp_client->timeout > 0) {
1113 g_source_remove(dhcp_client->timeout);
1114 dhcp_client->timeout = 0;
1117 if (dhcp_client->listener_watch > 0) {
1118 g_source_remove(dhcp_client->listener_watch);
1119 dhcp_client->listener_watch = 0;
1122 dhcp_client->listener_channel = NULL;
1124 dhcp_client->retry_times = 0;
1125 dhcp_client->ack_retry_times = 0;
1127 dhcp_client->requested_ip = 0;
1128 dhcp_client->state = RELEASED;
1129 dhcp_client->lease_seconds = 0;
1132 GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
1133 unsigned char option_code)
1135 return g_hash_table_lookup(dhcp_client->code_value_hash,
1136 GINT_TO_POINTER((int) option_code));
1139 void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
1140 GDHCPClientEvent event,
1141 GDHCPClientEventFunc func,
1145 case G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE:
1146 dhcp_client->lease_available_cb = func;
1147 dhcp_client->lease_available_data = data;
1149 case G_DHCP_CLIENT_EVENT_NO_LEASE:
1150 dhcp_client->no_lease_cb = func;
1151 dhcp_client->no_lease_data = data;
1153 case G_DHCP_CLIENT_EVENT_LEASE_LOST:
1154 dhcp_client->lease_lost_cb = func;
1155 dhcp_client->lease_lost_data = data;
1157 case G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT:
1158 dhcp_client->address_conflict_cb = func;
1159 dhcp_client->address_conflict_data = data;
1164 int g_dhcp_client_get_index(GDHCPClient *dhcp_client)
1166 return dhcp_client->ifindex;
1169 char *g_dhcp_client_get_address(GDHCPClient *dhcp_client)
1171 return g_strdup(dhcp_client->assigned_ip);
1174 GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
1175 unsigned char option_code)
1177 if (g_list_find(dhcp_client->request_list,
1178 GINT_TO_POINTER((int) option_code)) == NULL)
1179 dhcp_client->request_list = g_list_prepend(
1180 dhcp_client->request_list,
1181 (GINT_TO_POINTER((int) option_code)));
1183 return G_DHCP_CLIENT_ERROR_NONE;
1186 static uint8_t *alloc_dhcp_option(int code, const char *str, int extra)
1189 int len = strnlen(str, 255);
1191 storage = malloc(len + extra + OPT_DATA);
1192 storage[OPT_CODE] = code;
1193 storage[OPT_LEN] = len + extra;
1194 memcpy(storage + extra + OPT_DATA, str, len);
1199 static const char *get_hostname(const char *host)
1201 char local_host_name[HOST_NAME_MAX + 1];
1203 if (g_strcmp0("<hostname>", host) != 0)
1204 return g_strdup(host);
1206 if (gethostname(local_host_name, HOST_NAME_MAX) != 0)
1209 local_host_name[HOST_NAME_MAX] = 0;
1211 return g_strdup(local_host_name);
1214 /* Now only support send hostname */
1215 GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
1216 unsigned char option_code, const char *option_value)
1218 uint8_t *binary_option;
1219 const char *hostname;
1221 if (option_code == G_DHCP_HOST_NAME) {
1222 hostname = get_hostname(option_value);
1224 binary_option = alloc_dhcp_option(option_code, hostname, 0);
1226 g_hash_table_insert(dhcp_client->send_value_hash,
1227 GINT_TO_POINTER((int) option_code), binary_option);
1230 return G_DHCP_CLIENT_ERROR_NONE;
1233 GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client)
1235 if (dhcp_client == NULL)
1238 g_atomic_int_inc(&dhcp_client->ref_count);
1243 void g_dhcp_client_unref(GDHCPClient *dhcp_client)
1245 if (dhcp_client == NULL)
1248 if (g_atomic_int_dec_and_test(&dhcp_client->ref_count) == FALSE)
1251 g_dhcp_client_stop(dhcp_client);
1253 g_free(dhcp_client->interface);
1254 g_free(dhcp_client->assigned_ip);
1256 g_list_free(dhcp_client->request_list);
1257 g_list_free(dhcp_client->require_list);
1259 g_hash_table_destroy(dhcp_client->code_value_hash);
1260 g_hash_table_destroy(dhcp_client->send_value_hash);
1262 g_free(dhcp_client);
1265 void g_dhcp_client_set_debug(GDHCPClient *dhcp_client,
1266 GDHCPDebugFunc func, gpointer user_data)
1268 if (dhcp_client == NULL)
1271 dhcp_client->debug_func = func;
1272 dhcp_client->debug_data = user_data;