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 <netinet/if_ether.h>
36 #include <net/ethernet.h>
39 #include <linux/filter.h>
47 #define DISCOVER_TIMEOUT 3
48 #define DISCOVER_RETRIES 10
50 #define REQUEST_TIMEOUT 3
51 #define REQUEST_RETRIES 5
53 typedef enum _listen_mode {
60 typedef enum _dhcp_client_state {
79 uint8_t mac_address[6];
82 uint32_t requested_ip;
84 uint32_t lease_seconds;
85 ListenMode listen_mode;
88 uint8_t ack_retry_times;
92 GIOChannel *listener_channel;
95 GHashTable *code_value_hash;
96 GHashTable *send_value_hash;
97 GDHCPClientEventFunc lease_available_cb;
98 gpointer lease_available_data;
99 GDHCPClientEventFunc ipv4ll_available_cb;
100 gpointer ipv4ll_available_data;
101 GDHCPClientEventFunc no_lease_cb;
102 gpointer no_lease_data;
103 GDHCPClientEventFunc lease_lost_cb;
104 gpointer lease_lost_data;
105 GDHCPClientEventFunc ipv4ll_lost_cb;
106 gpointer ipv4ll_lost_data;
107 GDHCPClientEventFunc address_conflict_cb;
108 gpointer address_conflict_data;
109 GDHCPDebugFunc debug_func;
113 static inline void debug(GDHCPClient *client, const char *format, ...)
118 if (client->debug_func == NULL)
121 va_start(ap, format);
123 if (vsnprintf(str, sizeof(str), format, ap) > 0)
124 client->debug_func(str, client->debug_data);
129 /* Initialize the packet with the proper defaults */
130 static void init_packet(GDHCPClient *dhcp_client,
131 struct dhcp_packet *packet, char type)
133 dhcp_init_header(packet, type);
135 memcpy(packet->chaddr, dhcp_client->mac_address, 6);
138 static void add_request_options(GDHCPClient *dhcp_client,
139 struct dhcp_packet *packet)
144 int end = dhcp_end_option(packet->options);
146 for (list = dhcp_client->request_list; list; list = list->next) {
147 code = (uint8_t) GPOINTER_TO_INT(list->data);
149 packet->options[end + OPT_DATA + len] = code;
154 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
155 packet->options[end + OPT_LEN] = len;
156 packet->options[end + OPT_DATA + len] = DHCP_END;
160 static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
162 uint8_t *option = value;
163 struct dhcp_packet *packet = user_data;
165 dhcp_add_binary_option(packet, option);
168 static void add_send_options(GDHCPClient *dhcp_client,
169 struct dhcp_packet *packet)
171 g_hash_table_foreach(dhcp_client->send_value_hash,
172 add_binary_option, packet);
175 static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
177 struct dhcp_packet packet;
179 debug(dhcp_client, "sending DHCP discover request");
181 init_packet(dhcp_client, &packet, DHCPDISCOVER);
183 packet.xid = dhcp_client->xid;
186 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
188 /* Explicitly saying that we want RFC-compliant packets helps
189 * some buggy DHCP servers to NOT send bigger packets */
190 dhcp_add_simple_option(&packet, DHCP_MAX_SIZE, htons(576));
192 add_request_options(dhcp_client, &packet);
194 add_send_options(dhcp_client, &packet);
196 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
197 INADDR_BROADCAST, SERVER_PORT,
198 MAC_BCAST_ADDR, dhcp_client->ifindex);
201 static int send_select(GDHCPClient *dhcp_client)
203 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 addr.s_addr = dhcp_client->requested_ip;
222 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
223 INADDR_BROADCAST, SERVER_PORT,
224 MAC_BCAST_ADDR, dhcp_client->ifindex);
227 static int send_renew(GDHCPClient *dhcp_client)
229 struct dhcp_packet packet;
231 debug(dhcp_client, "sending DHCP renew request");
233 init_packet(dhcp_client , &packet, DHCPREQUEST);
234 packet.xid = dhcp_client->xid;
235 packet.ciaddr = dhcp_client->requested_ip;
237 add_request_options(dhcp_client, &packet);
239 add_send_options(dhcp_client, &packet);
241 return dhcp_send_kernel_packet(&packet,
242 dhcp_client->requested_ip, CLIENT_PORT,
243 dhcp_client->server_ip, SERVER_PORT);
246 static int send_rebound(GDHCPClient *dhcp_client)
248 struct dhcp_packet packet;
250 debug(dhcp_client, "sending DHCP rebound request");
252 init_packet(dhcp_client , &packet, DHCPREQUEST);
253 packet.xid = dhcp_client->xid;
254 packet.ciaddr = dhcp_client->requested_ip;
256 add_request_options(dhcp_client, &packet);
258 add_send_options(dhcp_client, &packet);
260 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
261 INADDR_BROADCAST, SERVER_PORT,
262 MAC_BCAST_ADDR, dhcp_client->ifindex);
265 static int send_release(GDHCPClient *dhcp_client,
266 uint32_t server, uint32_t ciaddr)
268 struct dhcp_packet packet;
270 debug(dhcp_client, "sending DHCP release request");
272 init_packet(dhcp_client, &packet, DHCPRELEASE);
274 packet.ciaddr = ciaddr;
276 dhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
278 return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
279 server, SERVER_PORT);
282 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data);
283 static int switch_listening_mode(GDHCPClient *dhcp_client,
284 ListenMode listen_mode);
286 static gboolean send_probe_packet(gpointer dhcp_data)
288 GDHCPClient *dhcp_client;
291 dhcp_client = dhcp_data;
292 /* if requested_ip is not valid, pick a new address*/
293 if (dhcp_client->requested_ip == 0) {
294 debug(dhcp_client, "pick a new random address");
295 dhcp_client->requested_ip = ipv4ll_random_ip(0);
298 debug(dhcp_client, "sending IPV4LL probe request");
300 if (dhcp_client->retry_times == 1) {
301 dhcp_client->state = IPV4LL_PROBE;
302 switch_listening_mode(dhcp_client, L_ARP);
304 ipv4ll_send_arp_packet(dhcp_client->mac_address, 0,
305 dhcp_client->requested_ip, dhcp_client->ifindex);
307 if (dhcp_client->retry_times < PROBE_NUM) {
308 /*add a random timeout in range of PROBE_MIN to PROBE_MAX*/
309 timeout = ipv4ll_random_delay_ms(PROBE_MAX-PROBE_MIN);
310 timeout += PROBE_MIN*1000;
312 timeout = (ANNOUNCE_WAIT * 1000);
314 dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
316 ipv4ll_probe_timeout,
322 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data);
323 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data);
325 static gboolean send_announce_packet(gpointer dhcp_data)
327 GDHCPClient *dhcp_client;
329 dhcp_client = dhcp_data;
331 debug(dhcp_client, "sending IPV4LL announce request");
333 ipv4ll_send_arp_packet(dhcp_client->mac_address,
334 dhcp_client->requested_ip,
335 dhcp_client->requested_ip,
336 dhcp_client->ifindex);
338 if (dhcp_client->timeout > 0)
339 g_source_remove(dhcp_client->timeout);
340 dhcp_client->timeout = 0;
342 if (dhcp_client->state == IPV4LL_DEFEND) {
343 dhcp_client->timeout =
344 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
346 ipv4ll_defend_timeout,
351 dhcp_client->timeout =
352 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
354 ipv4ll_announce_timeout,
360 static void get_interface_mac_address(int index, uint8_t *mac_address)
365 sk = socket(PF_INET, SOCK_DGRAM, 0);
367 perror("Open socket error");
371 memset(&ifr, 0, sizeof(ifr));
372 ifr.ifr_ifindex = index;
374 err = ioctl(sk, SIOCGIFNAME, &ifr);
376 perror("Get interface name error");
380 err = ioctl(sk, SIOCGIFHWADDR, &ifr);
382 perror("Get mac address error");
386 memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
392 static void remove_value(gpointer data, gpointer user_data)
398 static void remove_option_value(gpointer data)
400 GList *option_value = data;
402 g_list_foreach(option_value, remove_value, NULL);
405 GDHCPClient *g_dhcp_client_new(GDHCPType type,
406 int ifindex, GDHCPClientError *error)
408 GDHCPClient *dhcp_client;
411 *error = G_DHCP_CLIENT_ERROR_INVALID_INDEX;
415 dhcp_client = g_try_new0(GDHCPClient, 1);
416 if (dhcp_client == NULL) {
417 *error = G_DHCP_CLIENT_ERROR_NOMEM;
421 dhcp_client->interface = get_interface_name(ifindex);
422 if (dhcp_client->interface == NULL) {
423 *error = G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE;
427 if (interface_is_up(ifindex) == FALSE) {
428 *error = G_DHCP_CLIENT_ERROR_INTERFACE_DOWN;
432 get_interface_mac_address(ifindex, dhcp_client->mac_address);
434 dhcp_client->listener_sockfd = -1;
435 dhcp_client->listener_channel = NULL;
436 dhcp_client->listen_mode = L_NONE;
437 dhcp_client->ref_count = 1;
438 dhcp_client->type = type;
439 dhcp_client->ifindex = ifindex;
440 dhcp_client->lease_available_cb = NULL;
441 dhcp_client->ipv4ll_available_cb = NULL;
442 dhcp_client->no_lease_cb = NULL;
443 dhcp_client->lease_lost_cb = NULL;
444 dhcp_client->ipv4ll_lost_cb = NULL;
445 dhcp_client->address_conflict_cb = NULL;
446 dhcp_client->listener_watch = 0;
447 dhcp_client->retry_times = 0;
448 dhcp_client->ack_retry_times = 0;
449 dhcp_client->code_value_hash = g_hash_table_new_full(g_direct_hash,
450 g_direct_equal, NULL, remove_option_value);
451 dhcp_client->send_value_hash = g_hash_table_new_full(g_direct_hash,
452 g_direct_equal, NULL, g_free);
453 dhcp_client->request_list = NULL;
454 dhcp_client->require_list = NULL;
456 *error = G_DHCP_CLIENT_ERROR_NONE;
461 g_free(dhcp_client->interface);
466 #define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68)
468 static int dhcp_l2_socket(int ifindex)
471 struct sockaddr_ll sock;
476 * I've selected not to see LL header, so BPF doesn't see it, too.
477 * The filter may also pass non-IP and non-ARP packets, but we do
478 * a more complete check when receiving the message in userspace.
480 * and filter shamelessly stolen from:
482 * http://www.flamewarmaster.de/software/dhcpclient/
484 * There are a few other interesting ideas on that page (look under
485 * "Motivation"). Use of netlink events is most interesting. Think
486 * of various network servers listening for events and reconfiguring.
487 * That would obsolete sending HUP signals and/or make use of restarts.
489 * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
492 * TODO: make conditional?
494 static const struct sock_filter filter_instr[] = {
496 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
497 /* L5, L1, is UDP? */
498 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0),
499 /* ugly check for arp on ethernet-like and IPv4 */
500 BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */
501 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4),/* L3, L4 */
503 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */
504 /* check udp source and destination ports */
505 BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
507 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),
509 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* L3: pass */
510 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
513 static const struct sock_fprog filter_prog = {
514 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
515 /* casting const away: */
516 .filter = (struct sock_filter *) filter_instr,
519 fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
523 if (SERVER_PORT == 67 && CLIENT_PORT == 68)
524 /* Use only if standard ports are in use */
525 setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
526 sizeof(filter_prog));
528 memset(&sock, 0, sizeof(sock));
529 sock.sll_family = AF_PACKET;
530 sock.sll_protocol = htons(ETH_P_IP);
531 sock.sll_ifindex = ifindex;
533 if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
541 static gboolean sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
543 if (packet->ip.protocol != IPPROTO_UDP)
546 if (packet->ip.version != IPVERSION)
549 if (packet->ip.ihl != sizeof(packet->ip) >> 2)
552 if (packet->udp.dest != htons(CLIENT_PORT))
555 if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip)))
561 static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
564 struct ip_udp_dhcp_packet packet;
567 memset(&packet, 0, sizeof(packet));
569 bytes = read(fd, &packet, sizeof(packet));
573 if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
576 if (bytes < ntohs(packet.ip.tot_len))
577 /* packet is bigger than sizeof(packet), we did partial read */
580 /* ignore any extra garbage bytes */
581 bytes = ntohs(packet.ip.tot_len);
583 if (sanity_check(&packet, bytes) == FALSE)
586 check = packet.ip.check;
588 if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip)))
591 /* verify UDP checksum. IP header has to be modified for this */
592 memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
593 /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
594 packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
595 check = packet.udp.check;
596 packet.udp.check = 0;
597 if (check && check != dhcp_checksum(&packet, bytes))
600 memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) +
601 sizeof(packet.udp)));
603 if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
606 return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
609 static void ipv4ll_start(GDHCPClient *dhcp_client)
614 if (dhcp_client->timeout > 0) {
615 g_source_remove(dhcp_client->timeout);
616 dhcp_client->timeout = 0;
619 switch_listening_mode(dhcp_client, L_NONE);
620 dhcp_client->type = G_DHCP_IPV4LL;
621 dhcp_client->retry_times = 0;
622 dhcp_client->requested_ip = 0;
624 /*try to start with a based mac address ip*/
625 seed = (dhcp_client->mac_address[4] << 8 | dhcp_client->mac_address[4]);
626 dhcp_client->requested_ip = ipv4ll_random_ip(seed);
628 /*first wait a random delay to avoid storm of arp request on boot*/
629 timeout = ipv4ll_random_delay_ms(PROBE_WAIT);
631 dhcp_client->retry_times++;
632 dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
639 static void ipv4ll_stop(GDHCPClient *dhcp_client)
642 switch_listening_mode(dhcp_client, L_NONE);
644 if (dhcp_client->timeout > 0)
645 g_source_remove(dhcp_client->timeout);
647 if (dhcp_client->listener_watch > 0) {
648 g_source_remove(dhcp_client->listener_watch);
649 dhcp_client->listener_watch = 0;
652 dhcp_client->state = IPV4LL_PROBE;
653 dhcp_client->retry_times = 0;
654 dhcp_client->requested_ip = 0;
656 g_free(dhcp_client->assigned_ip);
657 dhcp_client->assigned_ip = NULL;
660 static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
663 struct ether_arp arp;
664 uint32_t ip_requested;
668 memset(&arp, 0, sizeof(arp));
670 bytes = read(dhcp_client->listener_sockfd, &arp, sizeof(arp));
674 if (arp.arp_op != htons(ARPOP_REPLY) &&
675 arp.arp_op != htons(ARPOP_REQUEST))
678 ip_requested = ntohl(dhcp_client->requested_ip);
679 source_conflict = !memcmp(arp.arp_spa, &ip_requested,
680 sizeof(ip_requested));
682 target_conflict = !memcmp(arp.arp_tpa, &ip_requested,
683 sizeof(ip_requested));
685 if (!source_conflict && !target_conflict)
688 dhcp_client->conflicts++;
690 debug(dhcp_client, "IPV4LL conflict detected");
692 if (dhcp_client->state == IPV4LL_MONITOR) {
693 if (!source_conflict)
695 dhcp_client->state = IPV4LL_DEFEND;
696 debug(dhcp_client, "DEFEND mode conflicts : %d",
697 dhcp_client->conflicts);
698 /*Try to defend with a single announce*/
699 send_announce_packet(dhcp_client);
703 if (dhcp_client->state == IPV4LL_DEFEND) {
704 if (!source_conflict)
706 else if (dhcp_client->ipv4ll_lost_cb != NULL)
707 dhcp_client->ipv4ll_lost_cb(dhcp_client,
708 dhcp_client->ipv4ll_lost_data);
711 ipv4ll_stop(dhcp_client);
713 if (dhcp_client->conflicts < MAX_CONFLICTS) {
714 /*restart whole state machine*/
715 dhcp_client->retry_times++;
716 dhcp_client->timeout =
717 g_timeout_add_full(G_PRIORITY_HIGH,
718 ipv4ll_random_delay_ms(PROBE_WAIT),
723 /* Here we got a lot of conflicts, RFC3927 states that we have
724 * to wait RATE_LIMIT_INTERVAL before retrying,
725 * but we just report failure.
727 else if (dhcp_client->no_lease_cb != NULL)
728 dhcp_client->no_lease_cb(dhcp_client,
729 dhcp_client->no_lease_data);
734 static gboolean check_package_owner(GDHCPClient *dhcp_client,
735 struct dhcp_packet *packet)
737 if (packet->xid != dhcp_client->xid)
740 if (packet->hlen != 6)
743 if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
749 static void start_request(GDHCPClient *dhcp_client);
751 static gboolean request_timeout(gpointer user_data)
753 GDHCPClient *dhcp_client = user_data;
755 debug(dhcp_client, "request timeout (retries %d)",
756 dhcp_client->retry_times);
758 dhcp_client->retry_times++;
760 start_request(dhcp_client);
765 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
768 static int switch_listening_mode(GDHCPClient *dhcp_client,
769 ListenMode listen_mode)
771 GIOChannel *listener_channel;
774 debug(dhcp_client, "switch listening mode (%d ==> %d)",
775 dhcp_client->listen_mode, listen_mode);
777 if (dhcp_client->listen_mode == listen_mode)
780 if (dhcp_client->listen_mode != L_NONE) {
781 g_source_remove(dhcp_client->listener_watch);
782 dhcp_client->listener_channel = NULL;
783 dhcp_client->listen_mode = L_NONE;
784 dhcp_client->listener_sockfd = -1;
785 dhcp_client->listener_watch = 0;
788 if (listen_mode == L_NONE)
791 if (listen_mode == L2)
792 listener_sockfd = dhcp_l2_socket(dhcp_client->ifindex);
793 else if (listen_mode == L3)
794 listener_sockfd = dhcp_l3_socket(CLIENT_PORT,
795 dhcp_client->interface);
796 else if (listen_mode == L_ARP)
797 listener_sockfd = ipv4ll_arp_socket(dhcp_client->ifindex);
801 if (listener_sockfd < 0)
804 listener_channel = g_io_channel_unix_new(listener_sockfd);
805 if (listener_channel == NULL) {
806 /* Failed to create listener channel */
807 close(listener_sockfd);
811 dhcp_client->listen_mode = listen_mode;
812 dhcp_client->listener_sockfd = listener_sockfd;
813 dhcp_client->listener_channel = listener_channel;
815 g_io_channel_set_close_on_unref(listener_channel, TRUE);
816 dhcp_client->listener_watch =
817 g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH,
818 G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
819 listener_event, dhcp_client,
821 g_io_channel_unref(dhcp_client->listener_channel);
826 static void start_request(GDHCPClient *dhcp_client)
828 debug(dhcp_client, "start request (retries %d)",
829 dhcp_client->retry_times);
831 if (dhcp_client->retry_times == REQUEST_RETRIES) {
832 dhcp_client->state = INIT_SELECTING;
833 ipv4ll_start(dhcp_client);
838 if (dhcp_client->retry_times == 0) {
839 dhcp_client->state = REQUESTING;
840 switch_listening_mode(dhcp_client, L2);
843 send_select(dhcp_client);
845 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
852 static uint32_t get_lease(struct dhcp_packet *packet)
855 uint32_t lease_seconds;
857 option_u8 = dhcp_get_option(packet, DHCP_LEASE_TIME);
858 if (option_u8 == NULL)
861 lease_seconds = dhcp_get_unaligned((uint32_t *) option_u8);
862 lease_seconds = ntohl(lease_seconds);
863 /* paranoia: must not be prone to overflows */
864 lease_seconds &= 0x0fffffff;
865 if (lease_seconds < 10)
868 return lease_seconds;
871 static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times)
873 debug(dhcp_client, "restart DHCP (retries %d)", retry_times);
875 if (dhcp_client->timeout > 0) {
876 g_source_remove(dhcp_client->timeout);
877 dhcp_client->timeout = 0;
880 dhcp_client->retry_times = retry_times;
881 dhcp_client->requested_ip = 0;
882 switch_listening_mode(dhcp_client, L2);
884 g_dhcp_client_start(dhcp_client);
887 static gboolean start_rebound_timeout(gpointer user_data)
889 GDHCPClient *dhcp_client = user_data;
891 debug(dhcp_client, "start rebound timeout");
893 switch_listening_mode(dhcp_client, L2);
895 dhcp_client->lease_seconds >>= 1;
897 /* We need to have enough time to receive ACK package*/
898 if (dhcp_client->lease_seconds <= 6) {
900 /* ip need to be cleared */
901 if (dhcp_client->lease_lost_cb != NULL)
902 dhcp_client->lease_lost_cb(dhcp_client,
903 dhcp_client->lease_lost_data);
905 restart_dhcp(dhcp_client, 0);
907 send_rebound(dhcp_client);
909 dhcp_client->timeout =
910 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
911 dhcp_client->lease_seconds >> 1,
912 start_rebound_timeout,
920 static void start_rebound(GDHCPClient *dhcp_client)
922 debug(dhcp_client, "start rebound");
924 dhcp_client->state = REBINDING;
926 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
927 dhcp_client->lease_seconds >> 1,
928 start_rebound_timeout,
933 static gboolean start_renew_timeout(gpointer user_data)
935 GDHCPClient *dhcp_client = user_data;
937 debug(dhcp_client, "start renew timeout");
939 dhcp_client->state = RENEWING;
941 dhcp_client->lease_seconds >>= 1;
943 switch_listening_mode(dhcp_client, L3);
944 if (dhcp_client->lease_seconds <= 60)
945 start_rebound(dhcp_client);
947 send_renew(dhcp_client);
949 dhcp_client->timeout =
950 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
951 dhcp_client->lease_seconds >> 1,
960 static void start_bound(GDHCPClient *dhcp_client)
962 debug(dhcp_client, "start bound");
964 dhcp_client->state = BOUND;
966 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
967 dhcp_client->lease_seconds >> 1,
968 start_renew_timeout, dhcp_client,
972 static gboolean restart_dhcp_timeout(gpointer user_data)
974 GDHCPClient *dhcp_client = user_data;
976 debug(dhcp_client, "restart DHCP timeout");
978 dhcp_client->ack_retry_times++;
980 restart_dhcp(dhcp_client, dhcp_client->ack_retry_times);
985 static char *get_ip(uint32_t ip)
991 return g_strdup(inet_ntoa(addr));
994 /* get a rough idea of how long an option will be */
995 static const uint8_t len_of_option_as_string[] = {
996 [OPTION_IP] = sizeof("255.255.255.255 "),
998 [OPTION_U8] = sizeof("255 "),
999 [OPTION_U16] = sizeof("65535 "),
1000 [OPTION_U32] = sizeof("4294967295 "),
1003 static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
1005 return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
1008 /* Create "opt_value1 option_value2 ..." string */
1009 static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
1011 unsigned upper_length;
1015 len = option[OPT_LEN - OPT_DATA];
1016 type &= OPTION_TYPE_MASK;
1017 optlen = dhcp_option_lengths[type];
1020 upper_length = len_of_option_as_string[type] *
1021 ((unsigned)len / (unsigned)optlen);
1022 dest = ret = malloc(upper_length + 1);
1026 while (len >= optlen) {
1029 dest += sprint_nip(dest, "", option);
1032 uint16_t val_u16 = dhcp_get_unaligned(
1033 (uint16_t *) option);
1034 dest += sprintf(dest, "%u", ntohs(val_u16));
1038 uint32_t val_u32 = dhcp_get_unaligned(
1039 (uint32_t *) option);
1040 dest += sprintf(dest, type == OPTION_U32 ? "%lu" :
1041 "%ld", (unsigned long) ntohl(val_u32));
1045 memcpy(dest, option, len);
1062 static GList *get_option_value_list(char *value, GDHCPOptionType type)
1070 if (type == OPTION_STRING)
1071 return g_list_append(list, g_strdup(value));
1073 while ((pos = strchr(pos, ' ')) != NULL) {
1076 list = g_list_append(list, g_strdup(value));
1081 list = g_list_append(list, g_strdup(value));
1086 static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
1088 GDHCPOptionType type;
1089 GList *list, *value_list;
1094 for (list = dhcp_client->request_list; list; list = list->next) {
1095 code = (uint8_t) GPOINTER_TO_INT(list->data);
1097 option = dhcp_get_option(packet, code);
1098 if (option == NULL) {
1099 g_hash_table_remove(dhcp_client->code_value_hash,
1100 GINT_TO_POINTER((int) code));
1104 type = dhcp_get_code_type(code);
1106 option_value = malloc_option_value_string(option, type);
1107 if (option_value == NULL)
1108 g_hash_table_remove(dhcp_client->code_value_hash,
1109 GINT_TO_POINTER((int) code));
1111 value_list = get_option_value_list(option_value, type);
1113 g_free(option_value);
1115 if (value_list == NULL)
1116 g_hash_table_remove(dhcp_client->code_value_hash,
1117 GINT_TO_POINTER((int) code));
1119 g_hash_table_insert(dhcp_client->code_value_hash,
1120 GINT_TO_POINTER((int) code), value_list);
1124 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
1127 GDHCPClient *dhcp_client = user_data;
1128 struct dhcp_packet packet;
1129 uint8_t *message_type, *option_u8;
1132 if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
1133 dhcp_client->listener_watch = 0;
1137 if (dhcp_client->listen_mode == L_NONE)
1140 if (dhcp_client->listen_mode == L2)
1141 re = dhcp_recv_l2_packet(&packet, dhcp_client->listener_sockfd);
1142 else if (dhcp_client->listen_mode == L3)
1143 re = dhcp_recv_l3_packet(&packet, dhcp_client->listener_sockfd);
1144 else if (dhcp_client->listen_mode == L_ARP) {
1145 re = ipv4ll_recv_arp_packet(dhcp_client);
1154 if (check_package_owner(dhcp_client, &packet) == FALSE)
1157 message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
1158 if (message_type == NULL)
1159 /* No message type option, ignore package */
1162 debug(dhcp_client, "received DHCP packet (current state %d)",
1163 dhcp_client->state);
1165 switch (dhcp_client->state) {
1166 case INIT_SELECTING:
1167 if (*message_type != DHCPOFFER)
1170 g_source_remove(dhcp_client->timeout);
1171 dhcp_client->timeout = 0;
1172 dhcp_client->retry_times = 0;
1174 option_u8 = dhcp_get_option(&packet, DHCP_SERVER_ID);
1175 dhcp_client->server_ip =
1176 dhcp_get_unaligned((uint32_t *) option_u8);
1177 dhcp_client->requested_ip = packet.yiaddr;
1179 dhcp_client->state = REQUESTING;
1181 start_request(dhcp_client);
1187 if (*message_type == DHCPACK) {
1188 dhcp_client->retry_times = 0;
1190 if (dhcp_client->timeout > 0)
1191 g_source_remove(dhcp_client->timeout);
1192 dhcp_client->timeout = 0;
1194 dhcp_client->lease_seconds = get_lease(&packet);
1196 get_request(dhcp_client, &packet);
1198 switch_listening_mode(dhcp_client, L_NONE);
1200 g_free(dhcp_client->assigned_ip);
1201 dhcp_client->assigned_ip = get_ip(packet.yiaddr);
1203 /* Address should be set up here */
1204 if (dhcp_client->lease_available_cb != NULL)
1205 dhcp_client->lease_available_cb(dhcp_client,
1206 dhcp_client->lease_available_data);
1208 start_bound(dhcp_client);
1209 } else if (*message_type == DHCPNAK) {
1210 dhcp_client->retry_times = 0;
1212 if (dhcp_client->timeout > 0)
1213 g_source_remove(dhcp_client->timeout);
1215 dhcp_client->timeout = g_timeout_add_seconds_full(
1217 restart_dhcp_timeout,
1227 debug(dhcp_client, "processed DHCP packet (new state %d)",
1228 dhcp_client->state);
1233 static gboolean discover_timeout(gpointer user_data)
1235 GDHCPClient *dhcp_client = user_data;
1237 dhcp_client->retry_times++;
1239 g_dhcp_client_start(dhcp_client);
1244 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data)
1246 GDHCPClient *dhcp_client = dhcp_data;
1248 debug(dhcp_client, "back to MONITOR mode");
1250 dhcp_client->conflicts = 0;
1251 dhcp_client->state = IPV4LL_MONITOR;
1256 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data)
1258 GDHCPClient *dhcp_client = dhcp_data;
1261 debug(dhcp_client, "request timeout (retries %d)",
1262 dhcp_client->retry_times);
1264 if (dhcp_client->retry_times != ANNOUNCE_NUM){
1265 dhcp_client->retry_times++;
1266 send_announce_packet(dhcp_client);
1270 ip = htonl(dhcp_client->requested_ip);
1271 debug(dhcp_client, "switching to monitor mode");
1272 dhcp_client->state = IPV4LL_MONITOR;
1273 dhcp_client->assigned_ip = get_ip(ip);
1275 if (dhcp_client->ipv4ll_available_cb != NULL)
1276 dhcp_client->ipv4ll_available_cb(dhcp_client,
1277 dhcp_client->ipv4ll_available_data);
1278 dhcp_client->conflicts = 0;
1283 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data)
1286 GDHCPClient *dhcp_client = dhcp_data;
1288 debug(dhcp_client, "IPV4LL probe timeout (retries %d)",
1289 dhcp_client->retry_times);
1291 if (dhcp_client->retry_times == PROBE_NUM) {
1292 dhcp_client->state = IPV4LL_ANNOUNCE;
1293 dhcp_client->retry_times = 0;
1295 dhcp_client->retry_times++;
1296 send_announce_packet(dhcp_client);
1299 dhcp_client->retry_times++;
1300 send_probe_packet(dhcp_client);
1305 int g_dhcp_client_start(GDHCPClient *dhcp_client)
1309 if (dhcp_client->retry_times == DISCOVER_RETRIES) {
1310 ipv4ll_start(dhcp_client);
1314 if (dhcp_client->retry_times == 0) {
1315 g_free(dhcp_client->assigned_ip);
1316 dhcp_client->assigned_ip = NULL;
1318 dhcp_client->state = INIT_SELECTING;
1319 re = switch_listening_mode(dhcp_client, L2);
1323 dhcp_client->xid = rand();
1326 send_discover(dhcp_client, 0);
1328 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1336 void g_dhcp_client_stop(GDHCPClient *dhcp_client)
1338 switch_listening_mode(dhcp_client, L_NONE);
1340 if (dhcp_client->state == BOUND ||
1341 dhcp_client->state == RENEWING ||
1342 dhcp_client->state == REBINDING)
1343 send_release(dhcp_client, dhcp_client->server_ip,
1344 dhcp_client->requested_ip);
1346 if (dhcp_client->timeout > 0) {
1347 g_source_remove(dhcp_client->timeout);
1348 dhcp_client->timeout = 0;
1351 if (dhcp_client->listener_watch > 0) {
1352 g_source_remove(dhcp_client->listener_watch);
1353 dhcp_client->listener_watch = 0;
1356 dhcp_client->listener_channel = NULL;
1358 dhcp_client->retry_times = 0;
1359 dhcp_client->ack_retry_times = 0;
1361 dhcp_client->requested_ip = 0;
1362 dhcp_client->state = RELEASED;
1363 dhcp_client->lease_seconds = 0;
1366 GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
1367 unsigned char option_code)
1369 return g_hash_table_lookup(dhcp_client->code_value_hash,
1370 GINT_TO_POINTER((int) option_code));
1373 void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
1374 GDHCPClientEvent event,
1375 GDHCPClientEventFunc func,
1379 case G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE:
1380 dhcp_client->lease_available_cb = func;
1381 dhcp_client->lease_available_data = data;
1383 case G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE:
1384 dhcp_client->ipv4ll_available_cb = func;
1385 dhcp_client->ipv4ll_available_data = data;
1387 case G_DHCP_CLIENT_EVENT_NO_LEASE:
1388 dhcp_client->no_lease_cb = func;
1389 dhcp_client->no_lease_data = data;
1391 case G_DHCP_CLIENT_EVENT_LEASE_LOST:
1392 dhcp_client->lease_lost_cb = func;
1393 dhcp_client->lease_lost_data = data;
1395 case G_DHCP_CLIENT_EVENT_IPV4LL_LOST:
1396 dhcp_client->ipv4ll_lost_cb = func;
1397 dhcp_client->ipv4ll_lost_data = data;
1399 case G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT:
1400 dhcp_client->address_conflict_cb = func;
1401 dhcp_client->address_conflict_data = data;
1406 int g_dhcp_client_get_index(GDHCPClient *dhcp_client)
1408 return dhcp_client->ifindex;
1411 char *g_dhcp_client_get_address(GDHCPClient *dhcp_client)
1413 return g_strdup(dhcp_client->assigned_ip);
1416 GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
1417 unsigned char option_code)
1419 if (g_list_find(dhcp_client->request_list,
1420 GINT_TO_POINTER((int) option_code)) == NULL)
1421 dhcp_client->request_list = g_list_prepend(
1422 dhcp_client->request_list,
1423 (GINT_TO_POINTER((int) option_code)));
1425 return G_DHCP_CLIENT_ERROR_NONE;
1428 static uint8_t *alloc_dhcp_option(int code, const char *str, int extra)
1431 int len = strnlen(str, 255);
1433 storage = malloc(len + extra + OPT_DATA);
1434 storage[OPT_CODE] = code;
1435 storage[OPT_LEN] = len + extra;
1436 memcpy(storage + extra + OPT_DATA, str, len);
1441 /* Now only support send hostname */
1442 GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
1443 unsigned char option_code, const char *option_value)
1445 uint8_t *binary_option;
1447 if (option_code == G_DHCP_HOST_NAME && option_value != NULL) {
1448 binary_option = alloc_dhcp_option(option_code,
1451 g_hash_table_insert(dhcp_client->send_value_hash,
1452 GINT_TO_POINTER((int) option_code), binary_option);
1455 return G_DHCP_CLIENT_ERROR_NONE;
1458 GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client)
1460 if (dhcp_client == NULL)
1463 g_atomic_int_inc(&dhcp_client->ref_count);
1468 void g_dhcp_client_unref(GDHCPClient *dhcp_client)
1470 if (dhcp_client == NULL)
1473 if (g_atomic_int_dec_and_test(&dhcp_client->ref_count) == FALSE)
1476 g_dhcp_client_stop(dhcp_client);
1478 g_free(dhcp_client->interface);
1479 g_free(dhcp_client->assigned_ip);
1481 g_list_free(dhcp_client->request_list);
1482 g_list_free(dhcp_client->require_list);
1484 g_hash_table_destroy(dhcp_client->code_value_hash);
1485 g_hash_table_destroy(dhcp_client->send_value_hash);
1487 g_free(dhcp_client);
1490 void g_dhcp_client_set_debug(GDHCPClient *dhcp_client,
1491 GDHCPDebugFunc func, gpointer user_data)
1493 if (dhcp_client == NULL)
1496 dhcp_client->debug_func = func;
1497 dhcp_client->debug_data = user_data;