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>
38 #include <linux/filter.h>
45 #define DISCOVER_TIMEOUT 3
46 #define DISCOVER_RETRIES 10
48 #define REQUEST_TIMEOUT 3
49 #define REQUEST_RETRIES 5
51 typedef enum _listen_mode {
57 typedef enum _dhcp_client_state {
72 uint8_t mac_address[6];
75 uint32_t requested_ip;
77 uint32_t lease_seconds;
78 ListenMode listen_mode;
81 uint8_t ack_retry_times;
84 GIOChannel *listener_channel;
87 GHashTable *code_value_hash;
88 GHashTable *send_value_hash;
89 GDHCPClientEventFunc lease_available_cb;
90 gpointer lease_available_data;
91 GDHCPClientEventFunc no_lease_cb;
92 gpointer no_lease_data;
93 GDHCPClientEventFunc lease_lost_cb;
94 gpointer lease_lost_data;
95 GDHCPClientEventFunc address_conflict_cb;
96 gpointer address_conflict_data;
97 GDHCPDebugFunc debug_func;
101 static inline void debug(GDHCPClient *client, const char *format, ...)
106 if (client->debug_func == NULL)
109 va_start(ap, format);
111 if (vsnprintf(str, sizeof(str), format, ap) > 0)
112 client->debug_func(str, client->debug_data);
117 /* Initialize the packet with the proper defaults */
118 static void init_packet(GDHCPClient *dhcp_client,
119 struct dhcp_packet *packet, char type)
121 dhcp_init_header(packet, type);
123 memcpy(packet->chaddr, dhcp_client->mac_address, 6);
126 static void add_request_options(GDHCPClient *dhcp_client,
127 struct dhcp_packet *packet)
132 int end = dhcp_end_option(packet->options);
134 for (list = dhcp_client->request_list; list; list = list->next) {
135 code = (uint8_t) GPOINTER_TO_INT(list->data);
137 packet->options[end + OPT_DATA + len] = code;
142 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
143 packet->options[end + OPT_LEN] = len;
144 packet->options[end + OPT_DATA + len] = DHCP_END;
148 static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
150 uint8_t *option = value;
151 struct dhcp_packet *packet = user_data;
153 dhcp_add_binary_option(packet, option);
156 static void add_send_options(GDHCPClient *dhcp_client,
157 struct dhcp_packet *packet)
159 g_hash_table_foreach(dhcp_client->send_value_hash,
160 add_binary_option, packet);
163 static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
165 struct dhcp_packet packet;
167 debug(dhcp_client, "sending DHCP discover request");
169 init_packet(dhcp_client, &packet, DHCPDISCOVER);
171 packet.xid = dhcp_client->xid;
174 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
176 /* Explicitly saying that we want RFC-compliant packets helps
177 * some buggy DHCP servers to NOT send bigger packets */
178 dhcp_add_simple_option(&packet, DHCP_MAX_SIZE, htons(576));
180 add_request_options(dhcp_client, &packet);
182 add_send_options(dhcp_client, &packet);
184 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
185 INADDR_BROADCAST, SERVER_PORT,
186 MAC_BCAST_ADDR, dhcp_client->ifindex);
189 static int send_select(GDHCPClient *dhcp_client)
191 struct dhcp_packet packet;
194 debug(dhcp_client, "sending DHCP select request");
196 init_packet(dhcp_client, &packet, DHCPREQUEST);
198 packet.xid = dhcp_client->xid;
200 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP,
201 dhcp_client->requested_ip);
202 dhcp_add_simple_option(&packet, DHCP_SERVER_ID, dhcp_client->server_ip);
204 add_request_options(dhcp_client, &packet);
206 add_send_options(dhcp_client, &packet);
208 addr.s_addr = dhcp_client->requested_ip;
210 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
211 INADDR_BROADCAST, SERVER_PORT,
212 MAC_BCAST_ADDR, dhcp_client->ifindex);
215 static int send_renew(GDHCPClient *dhcp_client)
217 struct dhcp_packet packet;
219 debug(dhcp_client, "sending DHCP renew request");
221 init_packet(dhcp_client , &packet, DHCPREQUEST);
222 packet.xid = dhcp_client->xid;
223 packet.ciaddr = dhcp_client->requested_ip;
225 add_request_options(dhcp_client, &packet);
227 add_send_options(dhcp_client, &packet);
229 return dhcp_send_kernel_packet(&packet,
230 dhcp_client->requested_ip, CLIENT_PORT,
231 dhcp_client->server_ip, SERVER_PORT);
234 static int send_rebound(GDHCPClient *dhcp_client)
236 struct dhcp_packet packet;
238 debug(dhcp_client, "sending DHCP rebound request");
240 init_packet(dhcp_client , &packet, DHCPREQUEST);
241 packet.xid = dhcp_client->xid;
242 packet.ciaddr = dhcp_client->requested_ip;
244 add_request_options(dhcp_client, &packet);
246 add_send_options(dhcp_client, &packet);
248 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
249 INADDR_BROADCAST, SERVER_PORT,
250 MAC_BCAST_ADDR, dhcp_client->ifindex);
253 static int send_release(GDHCPClient *dhcp_client,
254 uint32_t server, uint32_t ciaddr)
256 struct dhcp_packet packet;
258 debug(dhcp_client, "sending DHCP release request");
260 init_packet(dhcp_client, &packet, DHCPRELEASE);
262 packet.ciaddr = ciaddr;
264 dhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
266 return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
267 server, SERVER_PORT);
272 static void get_interface_mac_address(int index, uint8_t *mac_address)
277 sk = socket(PF_INET, SOCK_DGRAM, 0);
279 perror("Open socket error");
283 memset(&ifr, 0, sizeof(ifr));
284 ifr.ifr_ifindex = index;
286 err = ioctl(sk, SIOCGIFNAME, &ifr);
288 perror("Get interface name error");
292 err = ioctl(sk, SIOCGIFHWADDR, &ifr);
294 perror("Get mac address error");
298 memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
304 static void remove_value(gpointer data, gpointer user_data)
310 static void remove_option_value(gpointer data)
312 GList *option_value = data;
314 g_list_foreach(option_value, remove_value, NULL);
317 GDHCPClient *g_dhcp_client_new(GDHCPType type,
318 int ifindex, GDHCPClientError *error)
320 GDHCPClient *dhcp_client;
323 *error = G_DHCP_CLIENT_ERROR_INVALID_INDEX;
327 dhcp_client = g_try_new0(GDHCPClient, 1);
328 if (dhcp_client == NULL) {
329 *error = G_DHCP_CLIENT_ERROR_NOMEM;
333 dhcp_client->interface = get_interface_name(ifindex);
334 if (dhcp_client->interface == NULL) {
335 *error = G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE;
339 if (interface_is_up(ifindex) == FALSE) {
340 *error = G_DHCP_CLIENT_ERROR_INTERFACE_DOWN;
344 get_interface_mac_address(ifindex, dhcp_client->mac_address);
346 dhcp_client->listener_sockfd = -1;
347 dhcp_client->listener_channel = NULL;
348 dhcp_client->listen_mode = L_NONE;
349 dhcp_client->ref_count = 1;
350 dhcp_client->type = type;
351 dhcp_client->ifindex = ifindex;
352 dhcp_client->lease_available_cb = NULL;
353 dhcp_client->no_lease_cb = NULL;
354 dhcp_client->lease_lost_cb = NULL;
355 dhcp_client->address_conflict_cb = NULL;
356 dhcp_client->listener_watch = 0;
357 dhcp_client->retry_times = 0;
358 dhcp_client->ack_retry_times = 0;
359 dhcp_client->code_value_hash = g_hash_table_new_full(g_direct_hash,
360 g_direct_equal, NULL, remove_option_value);
361 dhcp_client->send_value_hash = g_hash_table_new_full(g_direct_hash,
362 g_direct_equal, NULL, g_free);
363 dhcp_client->request_list = NULL;
364 dhcp_client->require_list = NULL;
366 *error = G_DHCP_CLIENT_ERROR_NONE;
371 g_free(dhcp_client->interface);
376 #define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68)
378 static int dhcp_l2_socket(int ifindex)
381 struct sockaddr_ll sock;
386 * I've selected not to see LL header, so BPF doesn't see it, too.
387 * The filter may also pass non-IP and non-ARP packets, but we do
388 * a more complete check when receiving the message in userspace.
390 * and filter shamelessly stolen from:
392 * http://www.flamewarmaster.de/software/dhcpclient/
394 * There are a few other interesting ideas on that page (look under
395 * "Motivation"). Use of netlink events is most interesting. Think
396 * of various network servers listening for events and reconfiguring.
397 * That would obsolete sending HUP signals and/or make use of restarts.
399 * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
402 * TODO: make conditional?
404 static const struct sock_filter filter_instr[] = {
406 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
407 /* L5, L1, is UDP? */
408 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0),
409 /* ugly check for arp on ethernet-like and IPv4 */
410 BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */
411 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4),/* L3, L4 */
413 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */
414 /* check udp source and destination ports */
415 BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
417 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),
419 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* L3: pass */
420 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
423 static const struct sock_fprog filter_prog = {
424 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
425 /* casting const away: */
426 .filter = (struct sock_filter *) filter_instr,
429 fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
433 if (SERVER_PORT == 67 && CLIENT_PORT == 68)
434 /* Use only if standard ports are in use */
435 setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
436 sizeof(filter_prog));
438 memset(&sock, 0, sizeof(sock));
439 sock.sll_family = AF_PACKET;
440 sock.sll_protocol = htons(ETH_P_IP);
441 sock.sll_ifindex = ifindex;
443 if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
451 static gboolean sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
453 if (packet->ip.protocol != IPPROTO_UDP)
456 if (packet->ip.version != IPVERSION)
459 if (packet->ip.ihl != sizeof(packet->ip) >> 2)
462 if (packet->udp.dest != htons(CLIENT_PORT))
465 if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip)))
471 static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
474 struct ip_udp_dhcp_packet packet;
477 memset(&packet, 0, sizeof(packet));
479 bytes = read(fd, &packet, sizeof(packet));
483 if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
486 if (bytes < ntohs(packet.ip.tot_len))
487 /* packet is bigger than sizeof(packet), we did partial read */
490 /* ignore any extra garbage bytes */
491 bytes = ntohs(packet.ip.tot_len);
493 if (sanity_check(&packet, bytes) == FALSE)
496 check = packet.ip.check;
498 if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip)))
501 /* verify UDP checksum. IP header has to be modified for this */
502 memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
503 /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
504 packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
505 check = packet.udp.check;
506 packet.udp.check = 0;
507 if (check && check != dhcp_checksum(&packet, bytes))
510 memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) +
511 sizeof(packet.udp)));
513 if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
516 return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
519 static gboolean check_package_owner(GDHCPClient *dhcp_client,
520 struct dhcp_packet *packet)
522 if (packet->xid != dhcp_client->xid)
525 if (packet->hlen != 6)
528 if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
534 static void start_request(GDHCPClient *dhcp_client);
536 static gboolean request_timeout(gpointer user_data)
538 GDHCPClient *dhcp_client = user_data;
540 debug(dhcp_client, "request timeout (retries %d)",
541 dhcp_client->retry_times);
543 dhcp_client->retry_times++;
545 start_request(dhcp_client);
550 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
553 static int switch_listening_mode(GDHCPClient *dhcp_client,
554 ListenMode listen_mode)
556 GIOChannel *listener_channel;
559 debug(dhcp_client, "switch listening mode (%d ==> %d)",
560 dhcp_client->listen_mode, listen_mode);
562 if (dhcp_client->listen_mode == listen_mode)
565 if (dhcp_client->listen_mode != L_NONE) {
566 g_source_remove(dhcp_client->listener_watch);
567 dhcp_client->listener_channel = NULL;
568 dhcp_client->listen_mode = L_NONE;
569 dhcp_client->listener_sockfd = -1;
570 dhcp_client->listener_watch = 0;
573 if (listen_mode == L_NONE)
576 if (listen_mode == L2)
577 listener_sockfd = dhcp_l2_socket(dhcp_client->ifindex);
578 else if (listen_mode == L3)
579 listener_sockfd = dhcp_l3_socket(CLIENT_PORT,
580 dhcp_client->interface);
584 if (listener_sockfd < 0)
587 listener_channel = g_io_channel_unix_new(listener_sockfd);
588 if (listener_channel == NULL) {
589 /* Failed to create listener channel */
590 close(listener_sockfd);
594 dhcp_client->listen_mode = listen_mode;
595 dhcp_client->listener_sockfd = listener_sockfd;
596 dhcp_client->listener_channel = listener_channel;
598 g_io_channel_set_close_on_unref(listener_channel, TRUE);
599 dhcp_client->listener_watch =
600 g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH,
601 G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
602 listener_event, dhcp_client,
604 g_io_channel_unref(dhcp_client->listener_channel);
609 static void start_request(GDHCPClient *dhcp_client)
611 debug(dhcp_client, "start request (retries %d)",
612 dhcp_client->retry_times);
614 if (dhcp_client->retry_times == REQUEST_RETRIES) {
615 dhcp_client->state = INIT_SELECTING;
617 if (dhcp_client->no_lease_cb != NULL)
618 dhcp_client->no_lease_cb(dhcp_client,
619 dhcp_client->no_lease_data);
624 if (dhcp_client->retry_times == 0) {
625 dhcp_client->state = REQUESTING;
626 switch_listening_mode(dhcp_client, L2);
629 send_select(dhcp_client);
631 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
638 static uint32_t get_lease(struct dhcp_packet *packet)
641 uint32_t lease_seconds;
643 option_u8 = dhcp_get_option(packet, DHCP_LEASE_TIME);
644 if (option_u8 == NULL)
647 lease_seconds = dhcp_get_unaligned((uint32_t *) option_u8);
648 lease_seconds = ntohl(lease_seconds);
649 /* paranoia: must not be prone to overflows */
650 lease_seconds &= 0x0fffffff;
651 if (lease_seconds < 10)
654 return lease_seconds;
657 static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times)
659 debug(dhcp_client, "restart DHCP (retries %d)", retry_times);
661 if (dhcp_client->timeout > 0) {
662 g_source_remove(dhcp_client->timeout);
663 dhcp_client->timeout = 0;
666 dhcp_client->retry_times = retry_times;
667 dhcp_client->requested_ip = 0;
668 switch_listening_mode(dhcp_client, L2);
670 g_dhcp_client_start(dhcp_client);
673 static gboolean start_rebound_timeout(gpointer user_data)
675 GDHCPClient *dhcp_client = user_data;
677 debug(dhcp_client, "start rebound timeout");
679 switch_listening_mode(dhcp_client, L2);
681 dhcp_client->lease_seconds >>= 1;
683 /* We need to have enough time to receive ACK package*/
684 if (dhcp_client->lease_seconds <= 6) {
686 /* ip need to be cleared */
687 if (dhcp_client->lease_lost_cb != NULL)
688 dhcp_client->lease_lost_cb(dhcp_client,
689 dhcp_client->lease_lost_data);
691 restart_dhcp(dhcp_client, 0);
693 send_rebound(dhcp_client);
695 dhcp_client->timeout =
696 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
697 dhcp_client->lease_seconds >> 1,
698 start_rebound_timeout,
706 static void start_rebound(GDHCPClient *dhcp_client)
708 debug(dhcp_client, "start rebound");
710 dhcp_client->state = REBINDING;
712 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
713 dhcp_client->lease_seconds >> 1,
714 start_rebound_timeout,
719 static gboolean start_renew_timeout(gpointer user_data)
721 GDHCPClient *dhcp_client = user_data;
723 debug(dhcp_client, "start renew timeout");
725 dhcp_client->state = RENEWING;
727 dhcp_client->lease_seconds >>= 1;
729 switch_listening_mode(dhcp_client, L3);
730 if (dhcp_client->lease_seconds <= 60)
731 start_rebound(dhcp_client);
733 send_renew(dhcp_client);
735 dhcp_client->timeout =
736 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
737 dhcp_client->lease_seconds >> 1,
746 static void start_bound(GDHCPClient *dhcp_client)
748 debug(dhcp_client, "start bound");
750 dhcp_client->state = BOUND;
752 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
753 dhcp_client->lease_seconds >> 1,
754 start_renew_timeout, dhcp_client,
758 static gboolean restart_dhcp_timeout(gpointer user_data)
760 GDHCPClient *dhcp_client = user_data;
762 debug(dhcp_client, "restart DHCP timeout");
764 dhcp_client->ack_retry_times++;
766 restart_dhcp(dhcp_client, dhcp_client->ack_retry_times);
771 static char *get_ip(uint32_t ip)
777 return g_strdup(inet_ntoa(addr));
780 /* get a rough idea of how long an option will be */
781 static const uint8_t len_of_option_as_string[] = {
782 [OPTION_IP] = sizeof("255.255.255.255 "),
784 [OPTION_U8] = sizeof("255 "),
785 [OPTION_U16] = sizeof("65535 "),
786 [OPTION_U32] = sizeof("4294967295 "),
789 static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
791 return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
794 /* Create "opt_value1 option_value2 ..." string */
795 static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
797 unsigned upper_length;
801 len = option[OPT_LEN - OPT_DATA];
802 type &= OPTION_TYPE_MASK;
803 optlen = dhcp_option_lengths[type];
806 upper_length = len_of_option_as_string[type] *
807 ((unsigned)len / (unsigned)optlen);
808 dest = ret = malloc(upper_length + 1);
812 while (len >= optlen) {
815 dest += sprint_nip(dest, "", option);
818 uint16_t val_u16 = dhcp_get_unaligned(
819 (uint16_t *) option);
820 dest += sprintf(dest, "%u", ntohs(val_u16));
824 uint32_t val_u32 = dhcp_get_unaligned(
825 (uint32_t *) option);
826 dest += sprintf(dest, type == OPTION_U32 ? "%lu" :
827 "%ld", (unsigned long) ntohl(val_u32));
831 memcpy(dest, option, len);
848 static GList *get_option_value_list(char *value, GDHCPOptionType type)
856 if (type == OPTION_STRING)
857 return g_list_append(list, g_strdup(value));
859 while ((pos = strchr(pos, ' ')) != NULL) {
862 list = g_list_append(list, g_strdup(value));
867 list = g_list_append(list, g_strdup(value));
872 static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
874 GDHCPOptionType type;
875 GList *list, *value_list;
880 for (list = dhcp_client->request_list; list; list = list->next) {
881 code = (uint8_t) GPOINTER_TO_INT(list->data);
883 option = dhcp_get_option(packet, code);
884 if (option == NULL) {
885 g_hash_table_remove(dhcp_client->code_value_hash,
886 GINT_TO_POINTER((int) code));
890 type = dhcp_get_code_type(code);
892 option_value = malloc_option_value_string(option, type);
893 if (option_value == NULL)
894 g_hash_table_remove(dhcp_client->code_value_hash,
895 GINT_TO_POINTER((int) code));
897 value_list = get_option_value_list(option_value, type);
899 g_free(option_value);
901 if (value_list == NULL)
902 g_hash_table_remove(dhcp_client->code_value_hash,
903 GINT_TO_POINTER((int) code));
905 g_hash_table_insert(dhcp_client->code_value_hash,
906 GINT_TO_POINTER((int) code), value_list);
910 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
913 GDHCPClient *dhcp_client = user_data;
914 struct dhcp_packet packet;
915 uint8_t *message_type, *option_u8;
918 if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
919 dhcp_client->listener_watch = 0;
923 if (dhcp_client->listen_mode == L_NONE)
926 if (dhcp_client->listen_mode == L2)
927 re = dhcp_recv_l2_packet(&packet, dhcp_client->listener_sockfd);
928 else if (dhcp_client->listen_mode == L3)
929 re = dhcp_recv_l3_packet(&packet, dhcp_client->listener_sockfd);
936 if (check_package_owner(dhcp_client, &packet) == FALSE)
939 message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
940 if (message_type == NULL)
941 /* No message type option, ignore package */
944 debug(dhcp_client, "received DHCP packet (current state %d)",
947 switch (dhcp_client->state) {
949 if (*message_type != DHCPOFFER)
952 g_source_remove(dhcp_client->timeout);
953 dhcp_client->timeout = 0;
954 dhcp_client->retry_times = 0;
956 option_u8 = dhcp_get_option(&packet, DHCP_SERVER_ID);
957 dhcp_client->server_ip =
958 dhcp_get_unaligned((uint32_t *) option_u8);
959 dhcp_client->requested_ip = packet.yiaddr;
961 dhcp_client->state = REQUESTING;
963 start_request(dhcp_client);
969 if (*message_type == DHCPACK) {
970 dhcp_client->retry_times = 0;
972 if (dhcp_client->timeout > 0)
973 g_source_remove(dhcp_client->timeout);
974 dhcp_client->timeout = 0;
976 dhcp_client->lease_seconds = get_lease(&packet);
978 get_request(dhcp_client, &packet);
980 switch_listening_mode(dhcp_client, L_NONE);
982 g_free(dhcp_client->assigned_ip);
983 dhcp_client->assigned_ip = get_ip(packet.yiaddr);
985 /* Address should be set up here */
986 if (dhcp_client->lease_available_cb != NULL)
987 dhcp_client->lease_available_cb(dhcp_client,
988 dhcp_client->lease_available_data);
990 start_bound(dhcp_client);
991 } else if (*message_type == DHCPNAK) {
992 dhcp_client->retry_times = 0;
994 if (dhcp_client->timeout > 0)
995 g_source_remove(dhcp_client->timeout);
997 dhcp_client->timeout = g_timeout_add_seconds_full(
999 restart_dhcp_timeout,
1009 debug(dhcp_client, "processed DHCP packet (new state %d)",
1010 dhcp_client->state);
1015 static gboolean discover_timeout(gpointer user_data)
1017 GDHCPClient *dhcp_client = user_data;
1019 dhcp_client->retry_times++;
1021 g_dhcp_client_start(dhcp_client);
1026 int g_dhcp_client_start(GDHCPClient *dhcp_client)
1030 if (dhcp_client->retry_times == DISCOVER_RETRIES) {
1031 if (dhcp_client->no_lease_cb != NULL)
1032 dhcp_client->no_lease_cb(dhcp_client,
1033 dhcp_client->no_lease_data);
1038 if (dhcp_client->retry_times == 0) {
1039 g_free(dhcp_client->assigned_ip);
1040 dhcp_client->assigned_ip = NULL;
1042 dhcp_client->state = INIT_SELECTING;
1043 re = switch_listening_mode(dhcp_client, L2);
1047 dhcp_client->xid = rand();
1050 send_discover(dhcp_client, 0);
1052 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1060 void g_dhcp_client_stop(GDHCPClient *dhcp_client)
1062 switch_listening_mode(dhcp_client, L_NONE);
1064 if (dhcp_client->state == BOUND ||
1065 dhcp_client->state == RENEWING ||
1066 dhcp_client->state == REBINDING)
1067 send_release(dhcp_client, dhcp_client->server_ip,
1068 dhcp_client->requested_ip);
1070 if (dhcp_client->timeout > 0) {
1071 g_source_remove(dhcp_client->timeout);
1072 dhcp_client->timeout = 0;
1075 if (dhcp_client->listener_watch > 0) {
1076 g_source_remove(dhcp_client->listener_watch);
1077 dhcp_client->listener_watch = 0;
1080 dhcp_client->listener_channel = NULL;
1082 dhcp_client->retry_times = 0;
1083 dhcp_client->ack_retry_times = 0;
1085 dhcp_client->requested_ip = 0;
1086 dhcp_client->state = RELEASED;
1087 dhcp_client->lease_seconds = 0;
1090 GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
1091 unsigned char option_code)
1093 return g_hash_table_lookup(dhcp_client->code_value_hash,
1094 GINT_TO_POINTER((int) option_code));
1097 void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
1098 GDHCPClientEvent event,
1099 GDHCPClientEventFunc func,
1103 case G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE:
1104 dhcp_client->lease_available_cb = func;
1105 dhcp_client->lease_available_data = data;
1107 case G_DHCP_CLIENT_EVENT_NO_LEASE:
1108 dhcp_client->no_lease_cb = func;
1109 dhcp_client->no_lease_data = data;
1111 case G_DHCP_CLIENT_EVENT_LEASE_LOST:
1112 dhcp_client->lease_lost_cb = func;
1113 dhcp_client->lease_lost_data = data;
1115 case G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT:
1116 dhcp_client->address_conflict_cb = func;
1117 dhcp_client->address_conflict_data = data;
1122 int g_dhcp_client_get_index(GDHCPClient *dhcp_client)
1124 return dhcp_client->ifindex;
1127 char *g_dhcp_client_get_address(GDHCPClient *dhcp_client)
1129 return g_strdup(dhcp_client->assigned_ip);
1132 GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
1133 unsigned char option_code)
1135 if (g_list_find(dhcp_client->request_list,
1136 GINT_TO_POINTER((int) option_code)) == NULL)
1137 dhcp_client->request_list = g_list_prepend(
1138 dhcp_client->request_list,
1139 (GINT_TO_POINTER((int) option_code)));
1141 return G_DHCP_CLIENT_ERROR_NONE;
1144 static uint8_t *alloc_dhcp_option(int code, const char *str, int extra)
1147 int len = strnlen(str, 255);
1149 storage = malloc(len + extra + OPT_DATA);
1150 storage[OPT_CODE] = code;
1151 storage[OPT_LEN] = len + extra;
1152 memcpy(storage + extra + OPT_DATA, str, len);
1157 /* Now only support send hostname */
1158 GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
1159 unsigned char option_code, const char *option_value)
1161 uint8_t *binary_option;
1163 if (option_code == G_DHCP_HOST_NAME && option_value != NULL) {
1164 binary_option = alloc_dhcp_option(option_code,
1167 g_hash_table_insert(dhcp_client->send_value_hash,
1168 GINT_TO_POINTER((int) option_code), binary_option);
1171 return G_DHCP_CLIENT_ERROR_NONE;
1174 GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client)
1176 if (dhcp_client == NULL)
1179 g_atomic_int_inc(&dhcp_client->ref_count);
1184 void g_dhcp_client_unref(GDHCPClient *dhcp_client)
1186 if (dhcp_client == NULL)
1189 if (g_atomic_int_dec_and_test(&dhcp_client->ref_count) == FALSE)
1192 g_dhcp_client_stop(dhcp_client);
1194 g_free(dhcp_client->interface);
1195 g_free(dhcp_client->assigned_ip);
1197 g_list_free(dhcp_client->request_list);
1198 g_list_free(dhcp_client->require_list);
1200 g_hash_table_destroy(dhcp_client->code_value_hash);
1201 g_hash_table_destroy(dhcp_client->send_value_hash);
1203 g_free(dhcp_client);
1206 void g_dhcp_client_set_debug(GDHCPClient *dhcp_client,
1207 GDHCPDebugFunc func, gpointer user_data)
1209 if (dhcp_client == NULL)
1212 dhcp_client->debug_func = func;
1213 dhcp_client->debug_data = user_data;