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;
113 #if defined TIZEN_EXT
114 gboolean init_reboot;
118 static inline void debug(GDHCPClient *client, const char *format, ...)
123 if (client->debug_func == NULL)
126 va_start(ap, format);
128 if (vsnprintf(str, sizeof(str), format, ap) > 0)
129 client->debug_func(str, client->debug_data);
134 /* Initialize the packet with the proper defaults */
135 static void init_packet(GDHCPClient *dhcp_client,
136 struct dhcp_packet *packet, char type)
138 dhcp_init_header(packet, type);
140 memcpy(packet->chaddr, dhcp_client->mac_address, 6);
143 static void add_request_options(GDHCPClient *dhcp_client,
144 struct dhcp_packet *packet)
149 int end = dhcp_end_option(packet->options);
151 for (list = dhcp_client->request_list; list; list = list->next) {
152 code = (uint8_t) GPOINTER_TO_INT(list->data);
154 packet->options[end + OPT_DATA + len] = code;
159 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
160 packet->options[end + OPT_LEN] = len;
161 packet->options[end + OPT_DATA + len] = DHCP_END;
165 static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
167 uint8_t *option = value;
168 struct dhcp_packet *packet = user_data;
170 dhcp_add_binary_option(packet, option);
173 static void add_send_options(GDHCPClient *dhcp_client,
174 struct dhcp_packet *packet)
176 g_hash_table_foreach(dhcp_client->send_value_hash,
177 add_binary_option, packet);
180 static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
182 struct dhcp_packet packet;
184 debug(dhcp_client, "sending DHCP discover request");
186 init_packet(dhcp_client, &packet, DHCPDISCOVER);
188 packet.xid = dhcp_client->xid;
191 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
193 /* Explicitly saying that we want RFC-compliant packets helps
194 * some buggy DHCP servers to NOT send bigger packets */
195 dhcp_add_simple_option(&packet, DHCP_MAX_SIZE, htons(576));
197 add_request_options(dhcp_client, &packet);
199 add_send_options(dhcp_client, &packet);
201 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
202 INADDR_BROADCAST, SERVER_PORT,
203 MAC_BCAST_ADDR, dhcp_client->ifindex);
206 static int send_select(GDHCPClient *dhcp_client)
208 struct dhcp_packet packet;
210 debug(dhcp_client, "sending DHCP select request");
212 init_packet(dhcp_client, &packet, DHCPREQUEST);
214 packet.xid = dhcp_client->xid;
216 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP,
217 dhcp_client->requested_ip);
218 #if defined TIZEN_EXT
219 if (dhcp_client->init_reboot != TRUE)
221 dhcp_add_simple_option(&packet, DHCP_SERVER_ID, dhcp_client->server_ip);
223 add_request_options(dhcp_client, &packet);
225 add_send_options(dhcp_client, &packet);
227 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
228 INADDR_BROADCAST, SERVER_PORT,
229 MAC_BCAST_ADDR, dhcp_client->ifindex);
232 static int send_renew(GDHCPClient *dhcp_client)
234 struct dhcp_packet packet;
236 debug(dhcp_client, "sending DHCP renew request");
238 init_packet(dhcp_client , &packet, DHCPREQUEST);
239 packet.xid = dhcp_client->xid;
240 packet.ciaddr = dhcp_client->requested_ip;
242 add_request_options(dhcp_client, &packet);
244 add_send_options(dhcp_client, &packet);
246 return dhcp_send_kernel_packet(&packet,
247 dhcp_client->requested_ip, CLIENT_PORT,
248 dhcp_client->server_ip, SERVER_PORT);
251 static int send_rebound(GDHCPClient *dhcp_client)
253 struct dhcp_packet packet;
255 debug(dhcp_client, "sending DHCP rebound request");
257 init_packet(dhcp_client , &packet, DHCPREQUEST);
258 packet.xid = dhcp_client->xid;
259 packet.ciaddr = dhcp_client->requested_ip;
261 add_request_options(dhcp_client, &packet);
263 add_send_options(dhcp_client, &packet);
265 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
266 INADDR_BROADCAST, SERVER_PORT,
267 MAC_BCAST_ADDR, dhcp_client->ifindex);
270 static int send_release(GDHCPClient *dhcp_client,
271 uint32_t server, uint32_t ciaddr)
273 struct dhcp_packet packet;
275 debug(dhcp_client, "sending DHCP release request");
277 init_packet(dhcp_client, &packet, DHCPRELEASE);
279 packet.ciaddr = ciaddr;
281 dhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
283 return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
284 server, SERVER_PORT);
287 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data);
288 static int switch_listening_mode(GDHCPClient *dhcp_client,
289 ListenMode listen_mode);
291 static gboolean send_probe_packet(gpointer dhcp_data)
293 GDHCPClient *dhcp_client;
296 dhcp_client = dhcp_data;
297 /* if requested_ip is not valid, pick a new address*/
298 if (dhcp_client->requested_ip == 0) {
299 debug(dhcp_client, "pick a new random address");
300 dhcp_client->requested_ip = ipv4ll_random_ip(0);
303 debug(dhcp_client, "sending IPV4LL probe request");
305 if (dhcp_client->retry_times == 1) {
306 dhcp_client->state = IPV4LL_PROBE;
307 switch_listening_mode(dhcp_client, L_ARP);
309 ipv4ll_send_arp_packet(dhcp_client->mac_address, 0,
310 dhcp_client->requested_ip, dhcp_client->ifindex);
312 if (dhcp_client->retry_times < PROBE_NUM) {
313 /*add a random timeout in range of PROBE_MIN to PROBE_MAX*/
314 timeout = ipv4ll_random_delay_ms(PROBE_MAX-PROBE_MIN);
315 timeout += PROBE_MIN*1000;
317 timeout = (ANNOUNCE_WAIT * 1000);
319 dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
321 ipv4ll_probe_timeout,
327 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data);
328 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data);
330 static gboolean send_announce_packet(gpointer dhcp_data)
332 GDHCPClient *dhcp_client;
334 dhcp_client = dhcp_data;
336 debug(dhcp_client, "sending IPV4LL announce request");
338 ipv4ll_send_arp_packet(dhcp_client->mac_address,
339 dhcp_client->requested_ip,
340 dhcp_client->requested_ip,
341 dhcp_client->ifindex);
343 if (dhcp_client->timeout > 0)
344 g_source_remove(dhcp_client->timeout);
345 dhcp_client->timeout = 0;
347 if (dhcp_client->state == IPV4LL_DEFEND) {
348 dhcp_client->timeout =
349 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
351 ipv4ll_defend_timeout,
356 dhcp_client->timeout =
357 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
359 ipv4ll_announce_timeout,
365 static void get_interface_mac_address(int index, uint8_t *mac_address)
370 sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
372 perror("Open socket error");
376 memset(&ifr, 0, sizeof(ifr));
377 ifr.ifr_ifindex = index;
379 err = ioctl(sk, SIOCGIFNAME, &ifr);
381 perror("Get interface name error");
385 err = ioctl(sk, SIOCGIFHWADDR, &ifr);
387 perror("Get mac address error");
391 memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
397 static void remove_value(gpointer data, gpointer user_data)
403 static void remove_option_value(gpointer data)
405 GList *option_value = data;
407 g_list_foreach(option_value, remove_value, NULL);
410 GDHCPClient *g_dhcp_client_new(GDHCPType type,
411 int ifindex, GDHCPClientError *error)
413 GDHCPClient *dhcp_client;
416 *error = G_DHCP_CLIENT_ERROR_INVALID_INDEX;
420 dhcp_client = g_try_new0(GDHCPClient, 1);
421 if (dhcp_client == NULL) {
422 *error = G_DHCP_CLIENT_ERROR_NOMEM;
426 dhcp_client->interface = get_interface_name(ifindex);
427 if (dhcp_client->interface == NULL) {
428 *error = G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE;
432 if (interface_is_up(ifindex) == FALSE) {
433 *error = G_DHCP_CLIENT_ERROR_INTERFACE_DOWN;
437 get_interface_mac_address(ifindex, dhcp_client->mac_address);
439 dhcp_client->listener_sockfd = -1;
440 dhcp_client->listener_channel = NULL;
441 dhcp_client->listen_mode = L_NONE;
442 dhcp_client->ref_count = 1;
443 dhcp_client->type = type;
444 dhcp_client->ifindex = ifindex;
445 dhcp_client->lease_available_cb = NULL;
446 dhcp_client->ipv4ll_available_cb = NULL;
447 dhcp_client->no_lease_cb = NULL;
448 dhcp_client->lease_lost_cb = NULL;
449 dhcp_client->ipv4ll_lost_cb = NULL;
450 dhcp_client->address_conflict_cb = NULL;
451 dhcp_client->listener_watch = 0;
452 dhcp_client->retry_times = 0;
453 dhcp_client->ack_retry_times = 0;
454 dhcp_client->code_value_hash = g_hash_table_new_full(g_direct_hash,
455 g_direct_equal, NULL, remove_option_value);
456 dhcp_client->send_value_hash = g_hash_table_new_full(g_direct_hash,
457 g_direct_equal, NULL, g_free);
458 dhcp_client->request_list = NULL;
459 dhcp_client->require_list = NULL;
461 *error = G_DHCP_CLIENT_ERROR_NONE;
466 g_free(dhcp_client->interface);
471 #define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68)
473 static int dhcp_l2_socket(int ifindex)
476 struct sockaddr_ll sock;
481 * I've selected not to see LL header, so BPF doesn't see it, too.
482 * The filter may also pass non-IP and non-ARP packets, but we do
483 * a more complete check when receiving the message in userspace.
485 * and filter shamelessly stolen from:
487 * http://www.flamewarmaster.de/software/dhcpclient/
489 * There are a few other interesting ideas on that page (look under
490 * "Motivation"). Use of netlink events is most interesting. Think
491 * of various network servers listening for events and reconfiguring.
492 * That would obsolete sending HUP signals and/or make use of restarts.
494 * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
497 * TODO: make conditional?
499 static const struct sock_filter filter_instr[] = {
501 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
502 /* L5, L1, is UDP? */
503 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0),
504 /* ugly check for arp on ethernet-like and IPv4 */
505 BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */
506 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4),/* L3, L4 */
508 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */
509 /* check udp source and destination ports */
510 BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
512 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),
514 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* L3: pass */
515 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
518 static const struct sock_fprog filter_prog = {
519 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
520 /* casting const away: */
521 .filter = (struct sock_filter *) filter_instr,
524 fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
528 if (SERVER_PORT == 67 && CLIENT_PORT == 68)
529 /* Use only if standard ports are in use */
530 setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
531 sizeof(filter_prog));
533 memset(&sock, 0, sizeof(sock));
534 sock.sll_family = AF_PACKET;
535 sock.sll_protocol = htons(ETH_P_IP);
536 sock.sll_ifindex = ifindex;
538 if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
546 static gboolean sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
548 if (packet->ip.protocol != IPPROTO_UDP)
551 if (packet->ip.version != IPVERSION)
554 if (packet->ip.ihl != sizeof(packet->ip) >> 2)
557 if (packet->udp.dest != htons(CLIENT_PORT))
560 if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip)))
566 static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
569 struct ip_udp_dhcp_packet packet;
572 memset(&packet, 0, sizeof(packet));
574 bytes = read(fd, &packet, sizeof(packet));
578 if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
581 if (bytes < ntohs(packet.ip.tot_len))
582 /* packet is bigger than sizeof(packet), we did partial read */
585 /* ignore any extra garbage bytes */
586 bytes = ntohs(packet.ip.tot_len);
588 if (sanity_check(&packet, bytes) == FALSE)
591 check = packet.ip.check;
593 if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip)))
596 /* verify UDP checksum. IP header has to be modified for this */
597 memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
598 /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
599 packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
600 check = packet.udp.check;
601 packet.udp.check = 0;
602 if (check && check != dhcp_checksum(&packet, bytes))
605 memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) +
606 sizeof(packet.udp)));
608 if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
611 return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
614 static void ipv4ll_start(GDHCPClient *dhcp_client)
619 if (dhcp_client->timeout > 0) {
620 g_source_remove(dhcp_client->timeout);
621 dhcp_client->timeout = 0;
624 switch_listening_mode(dhcp_client, L_NONE);
625 dhcp_client->type = G_DHCP_IPV4LL;
626 dhcp_client->retry_times = 0;
627 dhcp_client->requested_ip = 0;
629 /*try to start with a based mac address ip*/
630 seed = (dhcp_client->mac_address[4] << 8 | dhcp_client->mac_address[4]);
631 dhcp_client->requested_ip = ipv4ll_random_ip(seed);
633 /*first wait a random delay to avoid storm of arp request on boot*/
634 timeout = ipv4ll_random_delay_ms(PROBE_WAIT);
636 dhcp_client->retry_times++;
637 dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
644 static void ipv4ll_stop(GDHCPClient *dhcp_client)
647 switch_listening_mode(dhcp_client, L_NONE);
649 if (dhcp_client->timeout > 0)
650 g_source_remove(dhcp_client->timeout);
652 if (dhcp_client->listener_watch > 0) {
653 g_source_remove(dhcp_client->listener_watch);
654 dhcp_client->listener_watch = 0;
657 dhcp_client->state = IPV4LL_PROBE;
658 dhcp_client->retry_times = 0;
659 dhcp_client->requested_ip = 0;
661 g_free(dhcp_client->assigned_ip);
662 dhcp_client->assigned_ip = NULL;
665 static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
668 struct ether_arp arp;
669 uint32_t ip_requested;
673 memset(&arp, 0, sizeof(arp));
675 bytes = read(dhcp_client->listener_sockfd, &arp, sizeof(arp));
679 if (arp.arp_op != htons(ARPOP_REPLY) &&
680 arp.arp_op != htons(ARPOP_REQUEST))
683 ip_requested = ntohl(dhcp_client->requested_ip);
684 source_conflict = !memcmp(arp.arp_spa, &ip_requested,
685 sizeof(ip_requested));
687 target_conflict = !memcmp(arp.arp_tpa, &ip_requested,
688 sizeof(ip_requested));
690 if (!source_conflict && !target_conflict)
693 dhcp_client->conflicts++;
695 debug(dhcp_client, "IPV4LL conflict detected");
697 if (dhcp_client->state == IPV4LL_MONITOR) {
698 if (!source_conflict)
700 dhcp_client->state = IPV4LL_DEFEND;
701 debug(dhcp_client, "DEFEND mode conflicts : %d",
702 dhcp_client->conflicts);
703 /*Try to defend with a single announce*/
704 send_announce_packet(dhcp_client);
708 if (dhcp_client->state == IPV4LL_DEFEND) {
709 if (!source_conflict)
711 else if (dhcp_client->ipv4ll_lost_cb != NULL)
712 dhcp_client->ipv4ll_lost_cb(dhcp_client,
713 dhcp_client->ipv4ll_lost_data);
716 ipv4ll_stop(dhcp_client);
718 if (dhcp_client->conflicts < MAX_CONFLICTS) {
719 /*restart whole state machine*/
720 dhcp_client->retry_times++;
721 dhcp_client->timeout =
722 g_timeout_add_full(G_PRIORITY_HIGH,
723 ipv4ll_random_delay_ms(PROBE_WAIT),
728 /* Here we got a lot of conflicts, RFC3927 states that we have
729 * to wait RATE_LIMIT_INTERVAL before retrying,
730 * but we just report failure.
732 else if (dhcp_client->no_lease_cb != NULL)
733 dhcp_client->no_lease_cb(dhcp_client,
734 dhcp_client->no_lease_data);
739 static gboolean check_package_owner(GDHCPClient *dhcp_client,
740 struct dhcp_packet *packet)
742 if (packet->xid != dhcp_client->xid)
745 if (packet->hlen != 6)
748 if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
754 static void start_request(GDHCPClient *dhcp_client);
756 static gboolean request_timeout(gpointer user_data)
758 GDHCPClient *dhcp_client = user_data;
760 debug(dhcp_client, "request timeout (retries %d)",
761 dhcp_client->retry_times);
763 dhcp_client->retry_times++;
765 start_request(dhcp_client);
770 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
773 static int switch_listening_mode(GDHCPClient *dhcp_client,
774 ListenMode listen_mode)
776 GIOChannel *listener_channel;
779 debug(dhcp_client, "switch listening mode (%d ==> %d)",
780 dhcp_client->listen_mode, listen_mode);
782 if (dhcp_client->listen_mode == listen_mode)
785 if (dhcp_client->listen_mode != L_NONE) {
786 g_source_remove(dhcp_client->listener_watch);
787 dhcp_client->listener_channel = NULL;
788 dhcp_client->listen_mode = L_NONE;
789 dhcp_client->listener_sockfd = -1;
790 dhcp_client->listener_watch = 0;
793 if (listen_mode == L_NONE)
796 if (listen_mode == L2)
797 listener_sockfd = dhcp_l2_socket(dhcp_client->ifindex);
798 else if (listen_mode == L3)
799 listener_sockfd = dhcp_l3_socket(CLIENT_PORT,
800 dhcp_client->interface);
801 else if (listen_mode == L_ARP)
802 listener_sockfd = ipv4ll_arp_socket(dhcp_client->ifindex);
806 if (listener_sockfd < 0)
809 listener_channel = g_io_channel_unix_new(listener_sockfd);
810 if (listener_channel == NULL) {
811 /* Failed to create listener channel */
812 close(listener_sockfd);
816 dhcp_client->listen_mode = listen_mode;
817 dhcp_client->listener_sockfd = listener_sockfd;
818 dhcp_client->listener_channel = listener_channel;
820 g_io_channel_set_close_on_unref(listener_channel, TRUE);
821 dhcp_client->listener_watch =
822 g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH,
823 G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
824 listener_event, dhcp_client,
826 g_io_channel_unref(dhcp_client->listener_channel);
831 static void start_request(GDHCPClient *dhcp_client)
833 debug(dhcp_client, "start request (retries %d)",
834 dhcp_client->retry_times);
836 if (dhcp_client->retry_times == REQUEST_RETRIES) {
837 dhcp_client->state = INIT_SELECTING;
838 ipv4ll_start(dhcp_client);
843 if (dhcp_client->retry_times == 0) {
844 dhcp_client->state = REQUESTING;
845 switch_listening_mode(dhcp_client, L2);
848 send_select(dhcp_client);
850 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
857 static uint32_t get_lease(struct dhcp_packet *packet)
860 uint32_t lease_seconds;
862 option_u8 = dhcp_get_option(packet, DHCP_LEASE_TIME);
863 if (option_u8 == NULL)
866 lease_seconds = dhcp_get_unaligned((uint32_t *) option_u8);
867 lease_seconds = ntohl(lease_seconds);
868 /* paranoia: must not be prone to overflows */
869 lease_seconds &= 0x0fffffff;
870 if (lease_seconds < 10)
873 return lease_seconds;
876 static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times)
878 debug(dhcp_client, "restart DHCP (retries %d)", retry_times);
880 if (dhcp_client->timeout > 0) {
881 g_source_remove(dhcp_client->timeout);
882 dhcp_client->timeout = 0;
885 dhcp_client->retry_times = retry_times;
886 dhcp_client->requested_ip = 0;
887 switch_listening_mode(dhcp_client, L2);
889 g_dhcp_client_start(dhcp_client, dhcp_client->last_address);
892 static gboolean start_rebound_timeout(gpointer user_data)
894 GDHCPClient *dhcp_client = user_data;
896 debug(dhcp_client, "start rebound timeout");
898 switch_listening_mode(dhcp_client, L2);
900 dhcp_client->lease_seconds >>= 1;
902 /* We need to have enough time to receive ACK package*/
903 if (dhcp_client->lease_seconds <= 6) {
905 /* ip need to be cleared */
906 if (dhcp_client->lease_lost_cb != NULL)
907 dhcp_client->lease_lost_cb(dhcp_client,
908 dhcp_client->lease_lost_data);
910 restart_dhcp(dhcp_client, 0);
912 send_rebound(dhcp_client);
914 dhcp_client->timeout =
915 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
916 dhcp_client->lease_seconds >> 1,
917 start_rebound_timeout,
925 static void start_rebound(GDHCPClient *dhcp_client)
927 debug(dhcp_client, "start rebound");
929 dhcp_client->state = REBINDING;
931 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
932 dhcp_client->lease_seconds >> 1,
933 start_rebound_timeout,
938 static gboolean start_renew_timeout(gpointer user_data)
940 GDHCPClient *dhcp_client = user_data;
942 debug(dhcp_client, "start renew timeout");
944 dhcp_client->state = RENEWING;
946 dhcp_client->lease_seconds >>= 1;
948 switch_listening_mode(dhcp_client, L3);
949 if (dhcp_client->lease_seconds <= 60)
950 start_rebound(dhcp_client);
952 send_renew(dhcp_client);
954 if (dhcp_client->timeout > 0)
955 g_source_remove(dhcp_client->timeout);
957 dhcp_client->timeout =
958 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
959 dhcp_client->lease_seconds >> 1,
968 static void start_bound(GDHCPClient *dhcp_client)
970 debug(dhcp_client, "start bound");
972 dhcp_client->state = BOUND;
974 if (dhcp_client->timeout > 0)
975 g_source_remove(dhcp_client->timeout);
977 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
978 dhcp_client->lease_seconds >> 1,
979 start_renew_timeout, dhcp_client,
983 static gboolean restart_dhcp_timeout(gpointer user_data)
985 GDHCPClient *dhcp_client = user_data;
987 debug(dhcp_client, "restart DHCP timeout");
989 dhcp_client->ack_retry_times++;
991 restart_dhcp(dhcp_client, dhcp_client->ack_retry_times);
996 static char *get_ip(uint32_t ip)
1002 return g_strdup(inet_ntoa(addr));
1005 /* get a rough idea of how long an option will be */
1006 static const uint8_t len_of_option_as_string[] = {
1007 [OPTION_IP] = sizeof("255.255.255.255 "),
1008 [OPTION_STRING] = 1,
1009 [OPTION_U8] = sizeof("255 "),
1010 [OPTION_U16] = sizeof("65535 "),
1011 [OPTION_U32] = sizeof("4294967295 "),
1014 static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
1016 return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
1019 /* Create "opt_value1 option_value2 ..." string */
1020 static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
1022 unsigned upper_length;
1026 len = option[OPT_LEN - OPT_DATA];
1027 type &= OPTION_TYPE_MASK;
1028 optlen = dhcp_option_lengths[type];
1031 upper_length = len_of_option_as_string[type] *
1032 ((unsigned)len / (unsigned)optlen);
1033 dest = ret = malloc(upper_length + 1);
1037 while (len >= optlen) {
1040 dest += sprint_nip(dest, "", option);
1043 uint16_t val_u16 = dhcp_get_unaligned(
1044 (uint16_t *) option);
1045 dest += sprintf(dest, "%u", ntohs(val_u16));
1049 uint32_t val_u32 = dhcp_get_unaligned(
1050 (uint32_t *) option);
1051 dest += sprintf(dest, type == OPTION_U32 ? "%lu" :
1052 "%ld", (unsigned long) ntohl(val_u32));
1056 memcpy(dest, option, len);
1073 static GList *get_option_value_list(char *value, GDHCPOptionType type)
1081 if (type == OPTION_STRING)
1082 return g_list_append(list, g_strdup(value));
1084 while ((pos = strchr(pos, ' ')) != NULL) {
1087 list = g_list_append(list, g_strdup(value));
1092 list = g_list_append(list, g_strdup(value));
1097 static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
1099 GDHCPOptionType type;
1100 GList *list, *value_list;
1105 for (list = dhcp_client->request_list; list; list = list->next) {
1106 code = (uint8_t) GPOINTER_TO_INT(list->data);
1108 option = dhcp_get_option(packet, code);
1109 if (option == NULL) {
1110 g_hash_table_remove(dhcp_client->code_value_hash,
1111 GINT_TO_POINTER((int) code));
1115 type = dhcp_get_code_type(code);
1117 option_value = malloc_option_value_string(option, type);
1118 if (option_value == NULL)
1119 g_hash_table_remove(dhcp_client->code_value_hash,
1120 GINT_TO_POINTER((int) code));
1122 value_list = get_option_value_list(option_value, type);
1124 g_free(option_value);
1126 if (value_list == NULL)
1127 g_hash_table_remove(dhcp_client->code_value_hash,
1128 GINT_TO_POINTER((int) code));
1130 g_hash_table_insert(dhcp_client->code_value_hash,
1131 GINT_TO_POINTER((int) code), value_list);
1135 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
1138 GDHCPClient *dhcp_client = user_data;
1139 struct dhcp_packet packet;
1140 uint8_t *message_type, *option_u8;
1143 if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
1144 dhcp_client->listener_watch = 0;
1148 if (dhcp_client->listen_mode == L_NONE)
1151 if (dhcp_client->listen_mode == L2)
1152 re = dhcp_recv_l2_packet(&packet, dhcp_client->listener_sockfd);
1153 else if (dhcp_client->listen_mode == L3)
1154 re = dhcp_recv_l3_packet(&packet, dhcp_client->listener_sockfd);
1155 else if (dhcp_client->listen_mode == L_ARP) {
1156 re = ipv4ll_recv_arp_packet(dhcp_client);
1165 if (check_package_owner(dhcp_client, &packet) == FALSE)
1168 message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
1169 if (message_type == NULL)
1170 /* No message type option, ignore package */
1173 debug(dhcp_client, "received DHCP packet (current state %d)",
1174 dhcp_client->state);
1176 switch (dhcp_client->state) {
1177 case INIT_SELECTING:
1178 if (*message_type != DHCPOFFER)
1181 g_source_remove(dhcp_client->timeout);
1182 dhcp_client->timeout = 0;
1183 dhcp_client->retry_times = 0;
1185 option_u8 = dhcp_get_option(&packet, DHCP_SERVER_ID);
1186 dhcp_client->server_ip =
1187 dhcp_get_unaligned((uint32_t *) option_u8);
1188 dhcp_client->requested_ip = packet.yiaddr;
1190 dhcp_client->state = REQUESTING;
1192 start_request(dhcp_client);
1198 if (*message_type == DHCPACK) {
1199 dhcp_client->retry_times = 0;
1201 if (dhcp_client->timeout > 0)
1202 g_source_remove(dhcp_client->timeout);
1203 dhcp_client->timeout = 0;
1205 dhcp_client->lease_seconds = get_lease(&packet);
1206 #if defined TIZEN_EXT
1207 debug(dhcp_client, "lease %d secs", dhcp_client->lease_seconds);
1210 get_request(dhcp_client, &packet);
1212 switch_listening_mode(dhcp_client, L_NONE);
1214 g_free(dhcp_client->assigned_ip);
1215 dhcp_client->assigned_ip = get_ip(packet.yiaddr);
1217 /* Address should be set up here */
1218 if (dhcp_client->lease_available_cb != NULL)
1219 dhcp_client->lease_available_cb(dhcp_client,
1220 dhcp_client->lease_available_data);
1222 start_bound(dhcp_client);
1223 } else if (*message_type == DHCPNAK) {
1224 dhcp_client->retry_times = 0;
1226 if (dhcp_client->timeout > 0)
1227 g_source_remove(dhcp_client->timeout);
1229 #if defined TIZEN_EXT
1230 g_dhcp_client_set_address_known(dhcp_client, FALSE);
1232 dhcp_client->timeout = g_timeout_add_seconds_full(
1234 restart_dhcp_timeout,
1244 debug(dhcp_client, "processed DHCP packet (new state %d)",
1245 dhcp_client->state);
1250 static gboolean discover_timeout(gpointer user_data)
1252 GDHCPClient *dhcp_client = user_data;
1254 dhcp_client->retry_times++;
1257 * We do not send the REQUESTED IP option if we are retrying because
1258 * if the server is non-authoritative it will ignore the request if the
1259 * option is present.
1261 g_dhcp_client_start(dhcp_client, NULL);
1266 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data)
1268 GDHCPClient *dhcp_client = dhcp_data;
1270 debug(dhcp_client, "back to MONITOR mode");
1272 dhcp_client->conflicts = 0;
1273 dhcp_client->state = IPV4LL_MONITOR;
1278 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data)
1280 GDHCPClient *dhcp_client = dhcp_data;
1283 debug(dhcp_client, "request timeout (retries %d)",
1284 dhcp_client->retry_times);
1286 if (dhcp_client->retry_times != ANNOUNCE_NUM){
1287 dhcp_client->retry_times++;
1288 send_announce_packet(dhcp_client);
1292 ip = htonl(dhcp_client->requested_ip);
1293 debug(dhcp_client, "switching to monitor mode");
1294 dhcp_client->state = IPV4LL_MONITOR;
1295 dhcp_client->assigned_ip = get_ip(ip);
1297 if (dhcp_client->ipv4ll_available_cb != NULL)
1298 dhcp_client->ipv4ll_available_cb(dhcp_client,
1299 dhcp_client->ipv4ll_available_data);
1300 dhcp_client->conflicts = 0;
1305 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data)
1308 GDHCPClient *dhcp_client = dhcp_data;
1310 debug(dhcp_client, "IPV4LL probe timeout (retries %d)",
1311 dhcp_client->retry_times);
1313 if (dhcp_client->retry_times == PROBE_NUM) {
1314 dhcp_client->state = IPV4LL_ANNOUNCE;
1315 dhcp_client->retry_times = 0;
1317 dhcp_client->retry_times++;
1318 send_announce_packet(dhcp_client);
1321 dhcp_client->retry_times++;
1322 send_probe_packet(dhcp_client);
1327 int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
1332 if (dhcp_client->retry_times == DISCOVER_RETRIES) {
1333 ipv4ll_start(dhcp_client);
1337 if (dhcp_client->retry_times == 0) {
1338 g_free(dhcp_client->assigned_ip);
1339 dhcp_client->assigned_ip = NULL;
1341 dhcp_client->state = INIT_SELECTING;
1342 re = switch_listening_mode(dhcp_client, L2);
1346 dhcp_client->xid = rand();
1349 if (last_address == NULL) {
1352 addr = inet_addr(last_address);
1353 if (addr == 0xFFFFFFFF) {
1356 g_free(dhcp_client->last_address);
1357 dhcp_client->last_address = g_strdup(last_address);
1360 #if defined TIZEN_EXT
1361 if (dhcp_client->init_reboot == TRUE) {
1362 dhcp_client->requested_ip = addr;
1364 start_request(dhcp_client);
1369 send_discover(dhcp_client, addr);
1371 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1379 void g_dhcp_client_stop(GDHCPClient *dhcp_client)
1381 switch_listening_mode(dhcp_client, L_NONE);
1383 if (dhcp_client->state == BOUND ||
1384 dhcp_client->state == RENEWING ||
1385 dhcp_client->state == REBINDING)
1386 send_release(dhcp_client, dhcp_client->server_ip,
1387 dhcp_client->requested_ip);
1389 if (dhcp_client->timeout > 0) {
1390 g_source_remove(dhcp_client->timeout);
1391 dhcp_client->timeout = 0;
1394 if (dhcp_client->listener_watch > 0) {
1395 g_source_remove(dhcp_client->listener_watch);
1396 dhcp_client->listener_watch = 0;
1399 dhcp_client->listener_channel = NULL;
1401 dhcp_client->retry_times = 0;
1402 dhcp_client->ack_retry_times = 0;
1404 dhcp_client->requested_ip = 0;
1405 dhcp_client->state = RELEASED;
1406 dhcp_client->lease_seconds = 0;
1409 GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
1410 unsigned char option_code)
1412 return g_hash_table_lookup(dhcp_client->code_value_hash,
1413 GINT_TO_POINTER((int) option_code));
1416 void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
1417 GDHCPClientEvent event,
1418 GDHCPClientEventFunc func,
1422 case G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE:
1423 dhcp_client->lease_available_cb = func;
1424 dhcp_client->lease_available_data = data;
1426 case G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE:
1427 dhcp_client->ipv4ll_available_cb = func;
1428 dhcp_client->ipv4ll_available_data = data;
1430 case G_DHCP_CLIENT_EVENT_NO_LEASE:
1431 dhcp_client->no_lease_cb = func;
1432 dhcp_client->no_lease_data = data;
1434 case G_DHCP_CLIENT_EVENT_LEASE_LOST:
1435 dhcp_client->lease_lost_cb = func;
1436 dhcp_client->lease_lost_data = data;
1438 case G_DHCP_CLIENT_EVENT_IPV4LL_LOST:
1439 dhcp_client->ipv4ll_lost_cb = func;
1440 dhcp_client->ipv4ll_lost_data = data;
1442 case G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT:
1443 dhcp_client->address_conflict_cb = func;
1444 dhcp_client->address_conflict_data = data;
1449 int g_dhcp_client_get_index(GDHCPClient *dhcp_client)
1451 return dhcp_client->ifindex;
1454 char *g_dhcp_client_get_address(GDHCPClient *dhcp_client)
1456 return g_strdup(dhcp_client->assigned_ip);
1459 char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
1461 GList *option = NULL;
1463 switch (dhcp_client->state) {
1465 case IPV4LL_MONITOR:
1466 return g_strdup("255.255.0.0");
1470 option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET);
1472 return g_strdup(option->data);
1473 case INIT_SELECTING:
1477 case IPV4LL_ANNOUNCE:
1483 GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
1484 unsigned char option_code)
1486 if (g_list_find(dhcp_client->request_list,
1487 GINT_TO_POINTER((int) option_code)) == NULL)
1488 dhcp_client->request_list = g_list_prepend(
1489 dhcp_client->request_list,
1490 (GINT_TO_POINTER((int) option_code)));
1492 return G_DHCP_CLIENT_ERROR_NONE;
1495 static uint8_t *alloc_dhcp_option(int code, const char *str, int extra)
1498 int len = strnlen(str, 255);
1500 storage = malloc(len + extra + OPT_DATA);
1501 storage[OPT_CODE] = code;
1502 storage[OPT_LEN] = len + extra;
1503 memcpy(storage + extra + OPT_DATA, str, len);
1508 /* Now only support send hostname */
1509 GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
1510 unsigned char option_code, const char *option_value)
1512 uint8_t *binary_option;
1514 if (option_code == G_DHCP_HOST_NAME && option_value != NULL) {
1515 binary_option = alloc_dhcp_option(option_code,
1518 g_hash_table_insert(dhcp_client->send_value_hash,
1519 GINT_TO_POINTER((int) option_code), binary_option);
1522 return G_DHCP_CLIENT_ERROR_NONE;
1525 GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client)
1527 if (dhcp_client == NULL)
1530 __sync_fetch_and_add(&dhcp_client->ref_count, 1);
1535 void g_dhcp_client_unref(GDHCPClient *dhcp_client)
1537 if (dhcp_client == NULL)
1540 if (__sync_fetch_and_sub(&dhcp_client->ref_count, 1) != 1)
1543 g_dhcp_client_stop(dhcp_client);
1545 g_free(dhcp_client->interface);
1546 g_free(dhcp_client->assigned_ip);
1547 g_free(dhcp_client->last_address);
1549 g_list_free(dhcp_client->request_list);
1550 g_list_free(dhcp_client->require_list);
1552 g_hash_table_destroy(dhcp_client->code_value_hash);
1553 g_hash_table_destroy(dhcp_client->send_value_hash);
1555 g_free(dhcp_client);
1558 void g_dhcp_client_set_debug(GDHCPClient *dhcp_client,
1559 GDHCPDebugFunc func, gpointer user_data)
1561 if (dhcp_client == NULL)
1564 dhcp_client->debug_func = func;
1565 dhcp_client->debug_data = user_data;
1568 #if defined TIZEN_EXT
1569 void g_dhcp_client_set_address_known(GDHCPClient *dhcp_client, gboolean known)
1571 /* DHCPREQUEST during INIT-REBOOT state (rfc2131)
1572 * 4.4.3 Initialization with known network address
1573 * 4.3.2 DHCPREQUEST generated during INIT-REBOOT state
1575 debug(dhcp_client, "known network address (%d)", known);
1577 if (dhcp_client->init_reboot == known)
1580 dhcp_client->init_reboot = known;