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
32 #include <sys/ioctl.h>
33 #include <arpa/inet.h>
35 #include <netpacket/packet.h>
36 #include <netinet/if_ether.h>
37 #include <net/ethernet.h>
40 #include <linux/filter.h>
48 #define DISCOVER_TIMEOUT 3
49 #define DISCOVER_RETRIES 10
51 #define REQUEST_TIMEOUT 3
52 #define REQUEST_RETRIES 5
54 typedef enum _listen_mode {
61 typedef enum _dhcp_client_state {
80 uint8_t mac_address[6];
83 uint32_t requested_ip;
85 uint32_t lease_seconds;
86 ListenMode listen_mode;
89 uint8_t ack_retry_times;
93 GIOChannel *listener_channel;
96 GHashTable *code_value_hash;
97 GHashTable *send_value_hash;
98 GDHCPClientEventFunc lease_available_cb;
99 gpointer lease_available_data;
100 GDHCPClientEventFunc ipv4ll_available_cb;
101 gpointer ipv4ll_available_data;
102 GDHCPClientEventFunc no_lease_cb;
103 gpointer no_lease_data;
104 GDHCPClientEventFunc lease_lost_cb;
105 gpointer lease_lost_data;
106 GDHCPClientEventFunc ipv4ll_lost_cb;
107 gpointer ipv4ll_lost_data;
108 GDHCPClientEventFunc address_conflict_cb;
109 gpointer address_conflict_data;
110 GDHCPDebugFunc debug_func;
114 static inline void debug(GDHCPClient *client, const char *format, ...)
119 if (client->debug_func == NULL)
122 va_start(ap, format);
124 if (vsnprintf(str, sizeof(str), format, ap) > 0)
125 client->debug_func(str, client->debug_data);
130 /* Initialize the packet with the proper defaults */
131 static void init_packet(GDHCPClient *dhcp_client,
132 struct dhcp_packet *packet, char type)
134 dhcp_init_header(packet, type);
136 memcpy(packet->chaddr, dhcp_client->mac_address, 6);
139 static void add_request_options(GDHCPClient *dhcp_client,
140 struct dhcp_packet *packet)
145 int end = dhcp_end_option(packet->options);
147 for (list = dhcp_client->request_list; list; list = list->next) {
148 code = (uint8_t) GPOINTER_TO_INT(list->data);
150 packet->options[end + OPT_DATA + len] = code;
155 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
156 packet->options[end + OPT_LEN] = len;
157 packet->options[end + OPT_DATA + len] = DHCP_END;
161 static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
163 uint8_t *option = value;
164 struct dhcp_packet *packet = user_data;
166 dhcp_add_binary_option(packet, option);
169 static void add_send_options(GDHCPClient *dhcp_client,
170 struct dhcp_packet *packet)
172 g_hash_table_foreach(dhcp_client->send_value_hash,
173 add_binary_option, packet);
176 static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
178 struct dhcp_packet packet;
180 debug(dhcp_client, "sending DHCP discover request");
182 init_packet(dhcp_client, &packet, DHCPDISCOVER);
184 packet.xid = dhcp_client->xid;
187 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
189 /* Explicitly saying that we want RFC-compliant packets helps
190 * some buggy DHCP servers to NOT send bigger packets */
191 dhcp_add_simple_option(&packet, DHCP_MAX_SIZE, htons(576));
193 add_request_options(dhcp_client, &packet);
195 add_send_options(dhcp_client, &packet);
197 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
198 INADDR_BROADCAST, SERVER_PORT,
199 MAC_BCAST_ADDR, dhcp_client->ifindex);
202 static int send_select(GDHCPClient *dhcp_client)
204 struct dhcp_packet packet;
206 debug(dhcp_client, "sending DHCP select request");
208 init_packet(dhcp_client, &packet, DHCPREQUEST);
210 packet.xid = dhcp_client->xid;
212 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP,
213 dhcp_client->requested_ip);
214 dhcp_add_simple_option(&packet, DHCP_SERVER_ID, dhcp_client->server_ip);
216 add_request_options(dhcp_client, &packet);
218 add_send_options(dhcp_client, &packet);
220 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
221 INADDR_BROADCAST, SERVER_PORT,
222 MAC_BCAST_ADDR, dhcp_client->ifindex);
225 static int send_renew(GDHCPClient *dhcp_client)
227 struct dhcp_packet packet;
229 debug(dhcp_client, "sending DHCP renew request");
231 init_packet(dhcp_client , &packet, DHCPREQUEST);
232 packet.xid = dhcp_client->xid;
233 packet.ciaddr = dhcp_client->requested_ip;
235 add_request_options(dhcp_client, &packet);
237 add_send_options(dhcp_client, &packet);
239 return dhcp_send_kernel_packet(&packet,
240 dhcp_client->requested_ip, CLIENT_PORT,
241 dhcp_client->server_ip, SERVER_PORT);
244 static int send_rebound(GDHCPClient *dhcp_client)
246 struct dhcp_packet packet;
248 debug(dhcp_client, "sending DHCP rebound request");
250 init_packet(dhcp_client , &packet, DHCPREQUEST);
251 packet.xid = dhcp_client->xid;
252 packet.ciaddr = dhcp_client->requested_ip;
254 add_request_options(dhcp_client, &packet);
256 add_send_options(dhcp_client, &packet);
258 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
259 INADDR_BROADCAST, SERVER_PORT,
260 MAC_BCAST_ADDR, dhcp_client->ifindex);
263 static int send_release(GDHCPClient *dhcp_client,
264 uint32_t server, uint32_t ciaddr)
266 struct dhcp_packet packet;
268 debug(dhcp_client, "sending DHCP release request");
270 init_packet(dhcp_client, &packet, DHCPRELEASE);
272 packet.ciaddr = ciaddr;
274 dhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
276 return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
277 server, SERVER_PORT);
280 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data);
281 static int switch_listening_mode(GDHCPClient *dhcp_client,
282 ListenMode listen_mode);
284 static gboolean send_probe_packet(gpointer dhcp_data)
286 GDHCPClient *dhcp_client;
289 dhcp_client = dhcp_data;
290 /* if requested_ip is not valid, pick a new address*/
291 if (dhcp_client->requested_ip == 0) {
292 debug(dhcp_client, "pick a new random address");
293 dhcp_client->requested_ip = ipv4ll_random_ip(0);
296 debug(dhcp_client, "sending IPV4LL probe request");
298 if (dhcp_client->retry_times == 1) {
299 dhcp_client->state = IPV4LL_PROBE;
300 switch_listening_mode(dhcp_client, L_ARP);
302 ipv4ll_send_arp_packet(dhcp_client->mac_address, 0,
303 dhcp_client->requested_ip, dhcp_client->ifindex);
305 if (dhcp_client->retry_times < PROBE_NUM) {
306 /*add a random timeout in range of PROBE_MIN to PROBE_MAX*/
307 timeout = ipv4ll_random_delay_ms(PROBE_MAX-PROBE_MIN);
308 timeout += PROBE_MIN*1000;
310 timeout = (ANNOUNCE_WAIT * 1000);
312 dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
314 ipv4ll_probe_timeout,
320 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data);
321 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data);
323 static gboolean send_announce_packet(gpointer dhcp_data)
325 GDHCPClient *dhcp_client;
327 dhcp_client = dhcp_data;
329 debug(dhcp_client, "sending IPV4LL announce request");
331 ipv4ll_send_arp_packet(dhcp_client->mac_address,
332 dhcp_client->requested_ip,
333 dhcp_client->requested_ip,
334 dhcp_client->ifindex);
336 if (dhcp_client->timeout > 0)
337 g_source_remove(dhcp_client->timeout);
338 dhcp_client->timeout = 0;
340 if (dhcp_client->state == IPV4LL_DEFEND) {
341 dhcp_client->timeout =
342 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
344 ipv4ll_defend_timeout,
349 dhcp_client->timeout =
350 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
352 ipv4ll_announce_timeout,
358 static void get_interface_mac_address(int index, uint8_t *mac_address)
363 sk = socket(PF_INET, SOCK_DGRAM, 0);
365 perror("Open socket error");
369 memset(&ifr, 0, sizeof(ifr));
370 ifr.ifr_ifindex = index;
372 err = ioctl(sk, SIOCGIFNAME, &ifr);
374 perror("Get interface name error");
378 err = ioctl(sk, SIOCGIFHWADDR, &ifr);
380 perror("Get mac address error");
384 memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
390 static void remove_value(gpointer data, gpointer user_data)
396 static void remove_option_value(gpointer data)
398 GList *option_value = data;
400 g_list_foreach(option_value, remove_value, NULL);
403 GDHCPClient *g_dhcp_client_new(GDHCPType type,
404 int ifindex, GDHCPClientError *error)
406 GDHCPClient *dhcp_client;
409 *error = G_DHCP_CLIENT_ERROR_INVALID_INDEX;
413 dhcp_client = g_try_new0(GDHCPClient, 1);
414 if (dhcp_client == NULL) {
415 *error = G_DHCP_CLIENT_ERROR_NOMEM;
419 dhcp_client->interface = get_interface_name(ifindex);
420 if (dhcp_client->interface == NULL) {
421 *error = G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE;
425 if (interface_is_up(ifindex) == FALSE) {
426 *error = G_DHCP_CLIENT_ERROR_INTERFACE_DOWN;
430 get_interface_mac_address(ifindex, dhcp_client->mac_address);
432 dhcp_client->listener_sockfd = -1;
433 dhcp_client->listener_channel = NULL;
434 dhcp_client->listen_mode = L_NONE;
435 dhcp_client->ref_count = 1;
436 dhcp_client->type = type;
437 dhcp_client->ifindex = ifindex;
438 dhcp_client->lease_available_cb = NULL;
439 dhcp_client->ipv4ll_available_cb = NULL;
440 dhcp_client->no_lease_cb = NULL;
441 dhcp_client->lease_lost_cb = NULL;
442 dhcp_client->ipv4ll_lost_cb = NULL;
443 dhcp_client->address_conflict_cb = NULL;
444 dhcp_client->listener_watch = 0;
445 dhcp_client->retry_times = 0;
446 dhcp_client->ack_retry_times = 0;
447 dhcp_client->code_value_hash = g_hash_table_new_full(g_direct_hash,
448 g_direct_equal, NULL, remove_option_value);
449 dhcp_client->send_value_hash = g_hash_table_new_full(g_direct_hash,
450 g_direct_equal, NULL, g_free);
451 dhcp_client->request_list = NULL;
452 dhcp_client->require_list = NULL;
454 *error = G_DHCP_CLIENT_ERROR_NONE;
459 g_free(dhcp_client->interface);
464 #define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68)
466 static int dhcp_l2_socket(int ifindex)
469 struct sockaddr_ll sock;
474 * I've selected not to see LL header, so BPF doesn't see it, too.
475 * The filter may also pass non-IP and non-ARP packets, but we do
476 * a more complete check when receiving the message in userspace.
478 * and filter shamelessly stolen from:
480 * http://www.flamewarmaster.de/software/dhcpclient/
482 * There are a few other interesting ideas on that page (look under
483 * "Motivation"). Use of netlink events is most interesting. Think
484 * of various network servers listening for events and reconfiguring.
485 * That would obsolete sending HUP signals and/or make use of restarts.
487 * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
490 * TODO: make conditional?
492 static const struct sock_filter filter_instr[] = {
494 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
495 /* L5, L1, is UDP? */
496 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0),
497 /* ugly check for arp on ethernet-like and IPv4 */
498 BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */
499 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4),/* L3, L4 */
501 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */
502 /* check udp source and destination ports */
503 BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
505 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),
507 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* L3: pass */
508 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
511 static const struct sock_fprog filter_prog = {
512 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
513 /* casting const away: */
514 .filter = (struct sock_filter *) filter_instr,
517 fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
521 if (SERVER_PORT == 67 && CLIENT_PORT == 68)
522 /* Use only if standard ports are in use */
523 setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
524 sizeof(filter_prog));
526 memset(&sock, 0, sizeof(sock));
527 sock.sll_family = AF_PACKET;
528 sock.sll_protocol = htons(ETH_P_IP);
529 sock.sll_ifindex = ifindex;
531 if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
539 static gboolean sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
541 if (packet->ip.protocol != IPPROTO_UDP)
544 if (packet->ip.version != IPVERSION)
547 if (packet->ip.ihl != sizeof(packet->ip) >> 2)
550 if (packet->udp.dest != htons(CLIENT_PORT))
553 if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip)))
559 static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
562 struct ip_udp_dhcp_packet packet;
565 memset(&packet, 0, sizeof(packet));
567 bytes = read(fd, &packet, sizeof(packet));
571 if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
574 if (bytes < ntohs(packet.ip.tot_len))
575 /* packet is bigger than sizeof(packet), we did partial read */
578 /* ignore any extra garbage bytes */
579 bytes = ntohs(packet.ip.tot_len);
581 if (sanity_check(&packet, bytes) == FALSE)
584 check = packet.ip.check;
586 if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip)))
589 /* verify UDP checksum. IP header has to be modified for this */
590 memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
591 /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
592 packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
593 check = packet.udp.check;
594 packet.udp.check = 0;
595 if (check && check != dhcp_checksum(&packet, bytes))
598 memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) +
599 sizeof(packet.udp)));
601 if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
604 return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
607 static void ipv4ll_start(GDHCPClient *dhcp_client)
612 if (dhcp_client->timeout > 0) {
613 g_source_remove(dhcp_client->timeout);
614 dhcp_client->timeout = 0;
617 switch_listening_mode(dhcp_client, L_NONE);
618 dhcp_client->type = G_DHCP_IPV4LL;
619 dhcp_client->retry_times = 0;
620 dhcp_client->requested_ip = 0;
622 /*try to start with a based mac address ip*/
623 seed = (dhcp_client->mac_address[4] << 8 | dhcp_client->mac_address[4]);
624 dhcp_client->requested_ip = ipv4ll_random_ip(seed);
626 /*first wait a random delay to avoid storm of arp request on boot*/
627 timeout = ipv4ll_random_delay_ms(PROBE_WAIT);
629 dhcp_client->retry_times++;
630 dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
637 static void ipv4ll_stop(GDHCPClient *dhcp_client)
640 switch_listening_mode(dhcp_client, L_NONE);
642 if (dhcp_client->timeout > 0)
643 g_source_remove(dhcp_client->timeout);
645 if (dhcp_client->listener_watch > 0) {
646 g_source_remove(dhcp_client->listener_watch);
647 dhcp_client->listener_watch = 0;
650 dhcp_client->state = IPV4LL_PROBE;
651 dhcp_client->retry_times = 0;
652 dhcp_client->requested_ip = 0;
654 g_free(dhcp_client->assigned_ip);
655 dhcp_client->assigned_ip = NULL;
658 static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
661 struct ether_arp arp;
662 uint32_t ip_requested;
666 memset(&arp, 0, sizeof(arp));
668 bytes = read(dhcp_client->listener_sockfd, &arp, sizeof(arp));
672 if (arp.arp_op != htons(ARPOP_REPLY) &&
673 arp.arp_op != htons(ARPOP_REQUEST))
676 ip_requested = ntohl(dhcp_client->requested_ip);
677 source_conflict = !memcmp(arp.arp_spa, &ip_requested,
678 sizeof(ip_requested));
680 target_conflict = !memcmp(arp.arp_tpa, &ip_requested,
681 sizeof(ip_requested));
683 if (!source_conflict && !target_conflict)
686 dhcp_client->conflicts++;
688 debug(dhcp_client, "IPV4LL conflict detected");
690 if (dhcp_client->state == IPV4LL_MONITOR) {
691 if (!source_conflict)
693 dhcp_client->state = IPV4LL_DEFEND;
694 debug(dhcp_client, "DEFEND mode conflicts : %d",
695 dhcp_client->conflicts);
696 /*Try to defend with a single announce*/
697 send_announce_packet(dhcp_client);
701 if (dhcp_client->state == IPV4LL_DEFEND) {
702 if (!source_conflict)
704 else if (dhcp_client->ipv4ll_lost_cb != NULL)
705 dhcp_client->ipv4ll_lost_cb(dhcp_client,
706 dhcp_client->ipv4ll_lost_data);
709 ipv4ll_stop(dhcp_client);
711 if (dhcp_client->conflicts < MAX_CONFLICTS) {
712 /*restart whole state machine*/
713 dhcp_client->retry_times++;
714 dhcp_client->timeout =
715 g_timeout_add_full(G_PRIORITY_HIGH,
716 ipv4ll_random_delay_ms(PROBE_WAIT),
721 /* Here we got a lot of conflicts, RFC3927 states that we have
722 * to wait RATE_LIMIT_INTERVAL before retrying,
723 * but we just report failure.
725 else if (dhcp_client->no_lease_cb != NULL)
726 dhcp_client->no_lease_cb(dhcp_client,
727 dhcp_client->no_lease_data);
732 static gboolean check_package_owner(GDHCPClient *dhcp_client,
733 struct dhcp_packet *packet)
735 if (packet->xid != dhcp_client->xid)
738 if (packet->hlen != 6)
741 if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
747 static void start_request(GDHCPClient *dhcp_client);
749 static gboolean request_timeout(gpointer user_data)
751 GDHCPClient *dhcp_client = user_data;
753 debug(dhcp_client, "request timeout (retries %d)",
754 dhcp_client->retry_times);
756 dhcp_client->retry_times++;
758 start_request(dhcp_client);
763 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
766 static int switch_listening_mode(GDHCPClient *dhcp_client,
767 ListenMode listen_mode)
769 GIOChannel *listener_channel;
772 debug(dhcp_client, "switch listening mode (%d ==> %d)",
773 dhcp_client->listen_mode, listen_mode);
775 if (dhcp_client->listen_mode == listen_mode)
778 if (dhcp_client->listen_mode != L_NONE) {
779 g_source_remove(dhcp_client->listener_watch);
780 dhcp_client->listener_channel = NULL;
781 dhcp_client->listen_mode = L_NONE;
782 dhcp_client->listener_sockfd = -1;
783 dhcp_client->listener_watch = 0;
786 if (listen_mode == L_NONE)
789 if (listen_mode == L2)
790 listener_sockfd = dhcp_l2_socket(dhcp_client->ifindex);
791 else if (listen_mode == L3)
792 listener_sockfd = dhcp_l3_socket(CLIENT_PORT,
793 dhcp_client->interface);
794 else if (listen_mode == L_ARP)
795 listener_sockfd = ipv4ll_arp_socket(dhcp_client->ifindex);
799 if (listener_sockfd < 0)
802 listener_channel = g_io_channel_unix_new(listener_sockfd);
803 if (listener_channel == NULL) {
804 /* Failed to create listener channel */
805 close(listener_sockfd);
809 dhcp_client->listen_mode = listen_mode;
810 dhcp_client->listener_sockfd = listener_sockfd;
811 dhcp_client->listener_channel = listener_channel;
813 g_io_channel_set_close_on_unref(listener_channel, TRUE);
814 dhcp_client->listener_watch =
815 g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH,
816 G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
817 listener_event, dhcp_client,
819 g_io_channel_unref(dhcp_client->listener_channel);
824 static void start_request(GDHCPClient *dhcp_client)
826 debug(dhcp_client, "start request (retries %d)",
827 dhcp_client->retry_times);
829 if (dhcp_client->retry_times == REQUEST_RETRIES) {
830 dhcp_client->state = INIT_SELECTING;
831 ipv4ll_start(dhcp_client);
836 if (dhcp_client->retry_times == 0) {
837 dhcp_client->state = REQUESTING;
838 switch_listening_mode(dhcp_client, L2);
841 send_select(dhcp_client);
843 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
850 static uint32_t get_lease(struct dhcp_packet *packet)
853 uint32_t lease_seconds;
855 option_u8 = dhcp_get_option(packet, DHCP_LEASE_TIME);
856 if (option_u8 == NULL)
859 lease_seconds = dhcp_get_unaligned((uint32_t *) option_u8);
860 lease_seconds = ntohl(lease_seconds);
861 /* paranoia: must not be prone to overflows */
862 lease_seconds &= 0x0fffffff;
863 if (lease_seconds < 10)
866 return lease_seconds;
869 static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times)
871 debug(dhcp_client, "restart DHCP (retries %d)", retry_times);
873 if (dhcp_client->timeout > 0) {
874 g_source_remove(dhcp_client->timeout);
875 dhcp_client->timeout = 0;
878 dhcp_client->retry_times = retry_times;
879 dhcp_client->requested_ip = 0;
880 switch_listening_mode(dhcp_client, L2);
882 g_dhcp_client_start(dhcp_client);
885 static gboolean start_rebound_timeout(gpointer user_data)
887 GDHCPClient *dhcp_client = user_data;
889 debug(dhcp_client, "start rebound timeout");
891 switch_listening_mode(dhcp_client, L2);
893 dhcp_client->lease_seconds >>= 1;
895 /* We need to have enough time to receive ACK package*/
896 if (dhcp_client->lease_seconds <= 6) {
898 /* ip need to be cleared */
899 if (dhcp_client->lease_lost_cb != NULL)
900 dhcp_client->lease_lost_cb(dhcp_client,
901 dhcp_client->lease_lost_data);
903 restart_dhcp(dhcp_client, 0);
905 send_rebound(dhcp_client);
907 dhcp_client->timeout =
908 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
909 dhcp_client->lease_seconds >> 1,
910 start_rebound_timeout,
918 static void start_rebound(GDHCPClient *dhcp_client)
920 debug(dhcp_client, "start rebound");
922 dhcp_client->state = REBINDING;
924 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
925 dhcp_client->lease_seconds >> 1,
926 start_rebound_timeout,
931 static gboolean start_renew_timeout(gpointer user_data)
933 GDHCPClient *dhcp_client = user_data;
935 debug(dhcp_client, "start renew timeout");
937 dhcp_client->state = RENEWING;
939 dhcp_client->lease_seconds >>= 1;
941 switch_listening_mode(dhcp_client, L3);
942 if (dhcp_client->lease_seconds <= 60)
943 start_rebound(dhcp_client);
945 send_renew(dhcp_client);
947 if (dhcp_client->timeout > 0)
948 g_source_remove(dhcp_client->timeout);
950 dhcp_client->timeout =
951 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
952 dhcp_client->lease_seconds >> 1,
961 static void start_bound(GDHCPClient *dhcp_client)
963 debug(dhcp_client, "start bound");
965 dhcp_client->state = BOUND;
967 if (dhcp_client->timeout > 0)
968 g_source_remove(dhcp_client->timeout);
970 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
971 dhcp_client->lease_seconds >> 1,
972 start_renew_timeout, dhcp_client,
976 static gboolean restart_dhcp_timeout(gpointer user_data)
978 GDHCPClient *dhcp_client = user_data;
980 debug(dhcp_client, "restart DHCP timeout");
982 dhcp_client->ack_retry_times++;
984 restart_dhcp(dhcp_client, dhcp_client->ack_retry_times);
989 static char *get_ip(uint32_t ip)
995 return g_strdup(inet_ntoa(addr));
998 /* get a rough idea of how long an option will be */
999 static const uint8_t len_of_option_as_string[] = {
1000 [OPTION_IP] = sizeof("255.255.255.255 "),
1001 [OPTION_STRING] = 1,
1002 [OPTION_U8] = sizeof("255 "),
1003 [OPTION_U16] = sizeof("65535 "),
1004 [OPTION_U32] = sizeof("4294967295 "),
1007 static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
1009 return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
1012 /* Create "opt_value1 option_value2 ..." string */
1013 static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
1015 unsigned upper_length;
1019 len = option[OPT_LEN - OPT_DATA];
1020 type &= OPTION_TYPE_MASK;
1021 optlen = dhcp_option_lengths[type];
1024 upper_length = len_of_option_as_string[type] *
1025 ((unsigned)len / (unsigned)optlen);
1026 dest = ret = malloc(upper_length + 1);
1030 while (len >= optlen) {
1033 dest += sprint_nip(dest, "", option);
1036 uint16_t val_u16 = dhcp_get_unaligned(
1037 (uint16_t *) option);
1038 dest += sprintf(dest, "%u", ntohs(val_u16));
1042 uint32_t val_u32 = dhcp_get_unaligned(
1043 (uint32_t *) option);
1044 dest += sprintf(dest, type == OPTION_U32 ? "%lu" :
1045 "%ld", (unsigned long) ntohl(val_u32));
1049 memcpy(dest, option, len);
1066 static GList *get_option_value_list(char *value, GDHCPOptionType type)
1074 if (type == OPTION_STRING)
1075 return g_list_append(list, g_strdup(value));
1077 while ((pos = strchr(pos, ' ')) != NULL) {
1080 list = g_list_append(list, g_strdup(value));
1085 list = g_list_append(list, g_strdup(value));
1090 static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
1092 GDHCPOptionType type;
1093 GList *list, *value_list;
1098 for (list = dhcp_client->request_list; list; list = list->next) {
1099 code = (uint8_t) GPOINTER_TO_INT(list->data);
1101 option = dhcp_get_option(packet, code);
1102 if (option == NULL) {
1103 g_hash_table_remove(dhcp_client->code_value_hash,
1104 GINT_TO_POINTER((int) code));
1108 type = dhcp_get_code_type(code);
1110 option_value = malloc_option_value_string(option, type);
1111 if (option_value == NULL)
1112 g_hash_table_remove(dhcp_client->code_value_hash,
1113 GINT_TO_POINTER((int) code));
1115 value_list = get_option_value_list(option_value, type);
1117 g_free(option_value);
1119 if (value_list == NULL)
1120 g_hash_table_remove(dhcp_client->code_value_hash,
1121 GINT_TO_POINTER((int) code));
1123 g_hash_table_insert(dhcp_client->code_value_hash,
1124 GINT_TO_POINTER((int) code), value_list);
1128 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
1131 GDHCPClient *dhcp_client = user_data;
1132 struct dhcp_packet packet;
1133 uint8_t *message_type, *option_u8;
1136 if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
1137 dhcp_client->listener_watch = 0;
1141 if (dhcp_client->listen_mode == L_NONE)
1144 if (dhcp_client->listen_mode == L2)
1145 re = dhcp_recv_l2_packet(&packet, dhcp_client->listener_sockfd);
1146 else if (dhcp_client->listen_mode == L3)
1147 re = dhcp_recv_l3_packet(&packet, dhcp_client->listener_sockfd);
1148 else if (dhcp_client->listen_mode == L_ARP) {
1149 re = ipv4ll_recv_arp_packet(dhcp_client);
1158 if (check_package_owner(dhcp_client, &packet) == FALSE)
1161 message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
1162 if (message_type == NULL)
1163 /* No message type option, ignore package */
1166 debug(dhcp_client, "received DHCP packet (current state %d)",
1167 dhcp_client->state);
1169 switch (dhcp_client->state) {
1170 case INIT_SELECTING:
1171 if (*message_type != DHCPOFFER)
1174 g_source_remove(dhcp_client->timeout);
1175 dhcp_client->timeout = 0;
1176 dhcp_client->retry_times = 0;
1178 option_u8 = dhcp_get_option(&packet, DHCP_SERVER_ID);
1179 dhcp_client->server_ip =
1180 dhcp_get_unaligned((uint32_t *) option_u8);
1181 dhcp_client->requested_ip = packet.yiaddr;
1183 dhcp_client->state = REQUESTING;
1185 start_request(dhcp_client);
1191 if (*message_type == DHCPACK) {
1192 dhcp_client->retry_times = 0;
1194 if (dhcp_client->timeout > 0)
1195 g_source_remove(dhcp_client->timeout);
1196 dhcp_client->timeout = 0;
1198 dhcp_client->lease_seconds = get_lease(&packet);
1200 get_request(dhcp_client, &packet);
1202 switch_listening_mode(dhcp_client, L_NONE);
1204 g_free(dhcp_client->assigned_ip);
1205 dhcp_client->assigned_ip = get_ip(packet.yiaddr);
1207 /* Address should be set up here */
1208 if (dhcp_client->lease_available_cb != NULL)
1209 dhcp_client->lease_available_cb(dhcp_client,
1210 dhcp_client->lease_available_data);
1212 start_bound(dhcp_client);
1213 } else if (*message_type == DHCPNAK) {
1214 dhcp_client->retry_times = 0;
1216 if (dhcp_client->timeout > 0)
1217 g_source_remove(dhcp_client->timeout);
1219 dhcp_client->timeout = g_timeout_add_seconds_full(
1221 restart_dhcp_timeout,
1231 debug(dhcp_client, "processed DHCP packet (new state %d)",
1232 dhcp_client->state);
1237 static gboolean discover_timeout(gpointer user_data)
1239 GDHCPClient *dhcp_client = user_data;
1241 dhcp_client->retry_times++;
1243 g_dhcp_client_start(dhcp_client);
1248 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data)
1250 GDHCPClient *dhcp_client = dhcp_data;
1252 debug(dhcp_client, "back to MONITOR mode");
1254 dhcp_client->conflicts = 0;
1255 dhcp_client->state = IPV4LL_MONITOR;
1260 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data)
1262 GDHCPClient *dhcp_client = dhcp_data;
1265 debug(dhcp_client, "request timeout (retries %d)",
1266 dhcp_client->retry_times);
1268 if (dhcp_client->retry_times != ANNOUNCE_NUM){
1269 dhcp_client->retry_times++;
1270 send_announce_packet(dhcp_client);
1274 ip = htonl(dhcp_client->requested_ip);
1275 debug(dhcp_client, "switching to monitor mode");
1276 dhcp_client->state = IPV4LL_MONITOR;
1277 dhcp_client->assigned_ip = get_ip(ip);
1279 if (dhcp_client->ipv4ll_available_cb != NULL)
1280 dhcp_client->ipv4ll_available_cb(dhcp_client,
1281 dhcp_client->ipv4ll_available_data);
1282 dhcp_client->conflicts = 0;
1287 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data)
1290 GDHCPClient *dhcp_client = dhcp_data;
1292 debug(dhcp_client, "IPV4LL probe timeout (retries %d)",
1293 dhcp_client->retry_times);
1295 if (dhcp_client->retry_times == PROBE_NUM) {
1296 dhcp_client->state = IPV4LL_ANNOUNCE;
1297 dhcp_client->retry_times = 0;
1299 dhcp_client->retry_times++;
1300 send_announce_packet(dhcp_client);
1303 dhcp_client->retry_times++;
1304 send_probe_packet(dhcp_client);
1309 int g_dhcp_client_start(GDHCPClient *dhcp_client)
1313 if (dhcp_client->retry_times == DISCOVER_RETRIES) {
1314 ipv4ll_start(dhcp_client);
1318 if (dhcp_client->retry_times == 0) {
1319 g_free(dhcp_client->assigned_ip);
1320 dhcp_client->assigned_ip = NULL;
1322 dhcp_client->state = INIT_SELECTING;
1323 re = switch_listening_mode(dhcp_client, L2);
1327 dhcp_client->xid = rand();
1330 send_discover(dhcp_client, 0);
1332 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1340 void g_dhcp_client_stop(GDHCPClient *dhcp_client)
1342 switch_listening_mode(dhcp_client, L_NONE);
1344 if (dhcp_client->state == BOUND ||
1345 dhcp_client->state == RENEWING ||
1346 dhcp_client->state == REBINDING)
1347 send_release(dhcp_client, dhcp_client->server_ip,
1348 dhcp_client->requested_ip);
1350 if (dhcp_client->timeout > 0) {
1351 g_source_remove(dhcp_client->timeout);
1352 dhcp_client->timeout = 0;
1355 if (dhcp_client->listener_watch > 0) {
1356 g_source_remove(dhcp_client->listener_watch);
1357 dhcp_client->listener_watch = 0;
1360 dhcp_client->listener_channel = NULL;
1362 dhcp_client->retry_times = 0;
1363 dhcp_client->ack_retry_times = 0;
1365 dhcp_client->requested_ip = 0;
1366 dhcp_client->state = RELEASED;
1367 dhcp_client->lease_seconds = 0;
1370 GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
1371 unsigned char option_code)
1373 return g_hash_table_lookup(dhcp_client->code_value_hash,
1374 GINT_TO_POINTER((int) option_code));
1377 void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
1378 GDHCPClientEvent event,
1379 GDHCPClientEventFunc func,
1383 case G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE:
1384 dhcp_client->lease_available_cb = func;
1385 dhcp_client->lease_available_data = data;
1387 case G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE:
1388 dhcp_client->ipv4ll_available_cb = func;
1389 dhcp_client->ipv4ll_available_data = data;
1391 case G_DHCP_CLIENT_EVENT_NO_LEASE:
1392 dhcp_client->no_lease_cb = func;
1393 dhcp_client->no_lease_data = data;
1395 case G_DHCP_CLIENT_EVENT_LEASE_LOST:
1396 dhcp_client->lease_lost_cb = func;
1397 dhcp_client->lease_lost_data = data;
1399 case G_DHCP_CLIENT_EVENT_IPV4LL_LOST:
1400 dhcp_client->ipv4ll_lost_cb = func;
1401 dhcp_client->ipv4ll_lost_data = data;
1403 case G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT:
1404 dhcp_client->address_conflict_cb = func;
1405 dhcp_client->address_conflict_data = data;
1410 int g_dhcp_client_get_index(GDHCPClient *dhcp_client)
1412 return dhcp_client->ifindex;
1415 char *g_dhcp_client_get_address(GDHCPClient *dhcp_client)
1417 return g_strdup(dhcp_client->assigned_ip);
1420 char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
1422 GList *option = NULL;
1424 switch (dhcp_client->state) {
1426 case IPV4LL_MONITOR:
1427 return g_strdup("255.255.0.0");
1431 option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET);
1433 return g_strdup(option->data);
1434 case INIT_SELECTING:
1438 case IPV4LL_ANNOUNCE:
1444 GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
1445 unsigned char option_code)
1447 if (g_list_find(dhcp_client->request_list,
1448 GINT_TO_POINTER((int) option_code)) == NULL)
1449 dhcp_client->request_list = g_list_prepend(
1450 dhcp_client->request_list,
1451 (GINT_TO_POINTER((int) option_code)));
1453 return G_DHCP_CLIENT_ERROR_NONE;
1456 static uint8_t *alloc_dhcp_option(int code, const char *str, int extra)
1459 int len = strnlen(str, 255);
1461 storage = malloc(len + extra + OPT_DATA);
1462 storage[OPT_CODE] = code;
1463 storage[OPT_LEN] = len + extra;
1464 memcpy(storage + extra + OPT_DATA, str, len);
1469 /* Now only support send hostname */
1470 GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
1471 unsigned char option_code, const char *option_value)
1473 uint8_t *binary_option;
1475 if (option_code == G_DHCP_HOST_NAME && option_value != NULL) {
1476 binary_option = alloc_dhcp_option(option_code,
1479 g_hash_table_insert(dhcp_client->send_value_hash,
1480 GINT_TO_POINTER((int) option_code), binary_option);
1483 return G_DHCP_CLIENT_ERROR_NONE;
1486 GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client)
1488 if (dhcp_client == NULL)
1491 g_atomic_int_inc(&dhcp_client->ref_count);
1496 void g_dhcp_client_unref(GDHCPClient *dhcp_client)
1498 if (dhcp_client == NULL)
1501 if (g_atomic_int_dec_and_test(&dhcp_client->ref_count) == FALSE)
1504 g_dhcp_client_stop(dhcp_client);
1506 g_free(dhcp_client->interface);
1507 g_free(dhcp_client->assigned_ip);
1509 g_list_free(dhcp_client->request_list);
1510 g_list_free(dhcp_client->require_list);
1512 g_hash_table_destroy(dhcp_client->code_value_hash);
1513 g_hash_table_destroy(dhcp_client->send_value_hash);
1515 g_free(dhcp_client);
1518 void g_dhcp_client_set_debug(GDHCPClient *dhcp_client,
1519 GDHCPDebugFunc func, gpointer user_data)
1521 if (dhcp_client == NULL)
1524 dhcp_client->debug_func = func;
1525 dhcp_client->debug_data = user_data;