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 {
81 uint8_t mac_address[6];
84 uint32_t requested_ip;
86 uint32_t lease_seconds;
87 ListenMode listen_mode;
90 uint8_t ack_retry_times;
94 GIOChannel *listener_channel;
97 GHashTable *code_value_hash;
98 GHashTable *send_value_hash;
99 GDHCPClientEventFunc lease_available_cb;
100 gpointer lease_available_data;
101 GDHCPClientEventFunc ipv4ll_available_cb;
102 gpointer ipv4ll_available_data;
103 GDHCPClientEventFunc no_lease_cb;
104 gpointer no_lease_data;
105 GDHCPClientEventFunc lease_lost_cb;
106 gpointer lease_lost_data;
107 GDHCPClientEventFunc ipv4ll_lost_cb;
108 gpointer ipv4ll_lost_data;
109 GDHCPClientEventFunc address_conflict_cb;
110 gpointer address_conflict_data;
111 GDHCPDebugFunc debug_func;
113 GDHCPClientEventFunc information_req_cb;
114 gpointer information_req_data;
118 uint16_t status_code;
121 static inline void debug(GDHCPClient *client, const char *format, ...)
126 if (client->debug_func == NULL)
129 va_start(ap, format);
131 if (vsnprintf(str, sizeof(str), format, ap) > 0)
132 client->debug_func(str, client->debug_data);
137 /* Initialize the packet with the proper defaults */
138 static void init_packet(GDHCPClient *dhcp_client, gpointer pkt, char type)
140 if (dhcp_client->type == G_DHCP_IPV6)
141 dhcpv6_init_header(pkt, type);
143 struct dhcp_packet *packet = pkt;
145 dhcp_init_header(packet, type);
146 memcpy(packet->chaddr, dhcp_client->mac_address, 6);
150 static void add_request_options(GDHCPClient *dhcp_client,
151 struct dhcp_packet *packet)
156 int end = dhcp_end_option(packet->options);
158 for (list = dhcp_client->request_list; list; list = list->next) {
159 code = (uint8_t) GPOINTER_TO_INT(list->data);
161 packet->options[end + OPT_DATA + len] = code;
166 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
167 packet->options[end + OPT_LEN] = len;
168 packet->options[end + OPT_DATA + len] = DHCP_END;
175 unsigned char **ptr_buf;
178 static void add_dhcpv6_binary_option(gpointer key, gpointer value,
181 uint8_t *option = value;
183 struct hash_params *params = user_data;
185 /* option[0][1] contains option code */
186 len = option[2] << 8 | option[3];
188 if ((*params->ptr_buf + len + 2 + 2) > (params->buf + params->max_buf))
191 memcpy(*params->ptr_buf, option, len + 2 + 2);
192 (*params->ptr_buf) += len + 2 + 2;
195 static void add_dhcpv6_send_options(GDHCPClient *dhcp_client,
196 unsigned char *buf, int max_buf,
197 unsigned char **ptr_buf)
199 struct hash_params params = {
205 if (dhcp_client->type == G_DHCP_IPV4)
208 g_hash_table_foreach(dhcp_client->send_value_hash,
209 add_dhcpv6_binary_option, ¶ms);
211 *ptr_buf = *params.ptr_buf;
214 static void copy_option(uint8_t *buf, uint16_t code, uint16_t len,
218 buf[1] = code & 0xff;
221 if (len > 0 && msg != NULL)
222 memcpy(&buf[4], msg, len);
225 static void add_dhcpv6_request_options(GDHCPClient *dhcp_client,
226 struct dhcpv6_packet *packet,
227 unsigned char *buf, int max_buf,
228 unsigned char **ptr_buf)
234 if (dhcp_client->type == G_DHCP_IPV4)
237 for (list = dhcp_client->request_list; list; list = list->next) {
238 code = (uint16_t) GPOINTER_TO_INT(list->data);
241 case G_DHCPV6_CLIENTID:
242 if (dhcp_client->duid == NULL)
245 len = 2 + 2 + dhcp_client->duid_len;
246 if ((*ptr_buf + len) > (buf + max_buf)) {
247 debug(dhcp_client, "Too long dhcpv6 message "
248 "when writing client id option");
252 copy_option(*ptr_buf, G_DHCPV6_CLIENTID,
253 dhcp_client->duid_len, dhcp_client->duid);
263 static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
265 uint8_t *option = value;
266 struct dhcp_packet *packet = user_data;
268 dhcp_add_binary_option(packet, option);
271 static void add_send_options(GDHCPClient *dhcp_client,
272 struct dhcp_packet *packet)
274 g_hash_table_foreach(dhcp_client->send_value_hash,
275 add_binary_option, packet);
278 static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
280 struct dhcp_packet packet;
282 debug(dhcp_client, "sending DHCP discover request");
284 init_packet(dhcp_client, &packet, DHCPDISCOVER);
286 packet.xid = dhcp_client->xid;
289 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
291 /* Explicitly saying that we want RFC-compliant packets helps
292 * some buggy DHCP servers to NOT send bigger packets */
293 dhcp_add_simple_option(&packet, DHCP_MAX_SIZE, htons(576));
295 add_request_options(dhcp_client, &packet);
297 add_send_options(dhcp_client, &packet);
299 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
300 INADDR_BROADCAST, SERVER_PORT,
301 MAC_BCAST_ADDR, dhcp_client->ifindex);
304 static int send_select(GDHCPClient *dhcp_client)
306 struct dhcp_packet packet;
308 debug(dhcp_client, "sending DHCP select request");
310 init_packet(dhcp_client, &packet, DHCPREQUEST);
312 packet.xid = dhcp_client->xid;
314 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP,
315 dhcp_client->requested_ip);
316 dhcp_add_simple_option(&packet, DHCP_SERVER_ID, dhcp_client->server_ip);
318 add_request_options(dhcp_client, &packet);
320 add_send_options(dhcp_client, &packet);
322 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
323 INADDR_BROADCAST, SERVER_PORT,
324 MAC_BCAST_ADDR, dhcp_client->ifindex);
327 static int send_renew(GDHCPClient *dhcp_client)
329 struct dhcp_packet packet;
331 debug(dhcp_client, "sending DHCP renew request");
333 init_packet(dhcp_client , &packet, DHCPREQUEST);
334 packet.xid = dhcp_client->xid;
335 packet.ciaddr = dhcp_client->requested_ip;
337 add_request_options(dhcp_client, &packet);
339 add_send_options(dhcp_client, &packet);
341 return dhcp_send_kernel_packet(&packet,
342 dhcp_client->requested_ip, CLIENT_PORT,
343 dhcp_client->server_ip, SERVER_PORT);
346 static int send_rebound(GDHCPClient *dhcp_client)
348 struct dhcp_packet packet;
350 debug(dhcp_client, "sending DHCP rebound request");
352 init_packet(dhcp_client , &packet, DHCPREQUEST);
353 packet.xid = dhcp_client->xid;
354 packet.ciaddr = dhcp_client->requested_ip;
356 add_request_options(dhcp_client, &packet);
358 add_send_options(dhcp_client, &packet);
360 return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
361 INADDR_BROADCAST, SERVER_PORT,
362 MAC_BCAST_ADDR, dhcp_client->ifindex);
365 static int send_release(GDHCPClient *dhcp_client,
366 uint32_t server, uint32_t ciaddr)
368 struct dhcp_packet packet;
370 debug(dhcp_client, "sending DHCP release request");
372 init_packet(dhcp_client, &packet, DHCPRELEASE);
374 packet.ciaddr = ciaddr;
376 dhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
378 return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
379 server, SERVER_PORT);
382 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data);
383 static int switch_listening_mode(GDHCPClient *dhcp_client,
384 ListenMode listen_mode);
386 static gboolean send_probe_packet(gpointer dhcp_data)
388 GDHCPClient *dhcp_client;
391 dhcp_client = dhcp_data;
392 /* if requested_ip is not valid, pick a new address*/
393 if (dhcp_client->requested_ip == 0) {
394 debug(dhcp_client, "pick a new random address");
395 dhcp_client->requested_ip = ipv4ll_random_ip(0);
398 debug(dhcp_client, "sending IPV4LL probe request");
400 if (dhcp_client->retry_times == 1) {
401 dhcp_client->state = IPV4LL_PROBE;
402 switch_listening_mode(dhcp_client, L_ARP);
404 ipv4ll_send_arp_packet(dhcp_client->mac_address, 0,
405 dhcp_client->requested_ip, dhcp_client->ifindex);
407 if (dhcp_client->retry_times < PROBE_NUM) {
408 /*add a random timeout in range of PROBE_MIN to PROBE_MAX*/
409 timeout = ipv4ll_random_delay_ms(PROBE_MAX-PROBE_MIN);
410 timeout += PROBE_MIN*1000;
412 timeout = (ANNOUNCE_WAIT * 1000);
414 dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
416 ipv4ll_probe_timeout,
422 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data);
423 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data);
425 static gboolean send_announce_packet(gpointer dhcp_data)
427 GDHCPClient *dhcp_client;
429 dhcp_client = dhcp_data;
431 debug(dhcp_client, "sending IPV4LL announce request");
433 ipv4ll_send_arp_packet(dhcp_client->mac_address,
434 dhcp_client->requested_ip,
435 dhcp_client->requested_ip,
436 dhcp_client->ifindex);
438 if (dhcp_client->timeout > 0)
439 g_source_remove(dhcp_client->timeout);
440 dhcp_client->timeout = 0;
442 if (dhcp_client->state == IPV4LL_DEFEND) {
443 dhcp_client->timeout =
444 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
446 ipv4ll_defend_timeout,
451 dhcp_client->timeout =
452 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
454 ipv4ll_announce_timeout,
460 static void get_interface_mac_address(int index, uint8_t *mac_address)
465 sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
467 perror("Open socket error");
471 memset(&ifr, 0, sizeof(ifr));
472 ifr.ifr_ifindex = index;
474 err = ioctl(sk, SIOCGIFNAME, &ifr);
476 perror("Get interface name error");
480 err = ioctl(sk, SIOCGIFHWADDR, &ifr);
482 perror("Get mac address error");
486 memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
492 int g_dhcpv6_create_duid(GDHCPDuidType duid_type, int index, int type,
493 unsigned char **duid, int *duid_len)
498 case G_DHCPV6_DUID_LLT:
499 *duid_len = 2 + 2 + 4 + ETH_ALEN;
500 *duid = g_try_malloc(*duid_len);
506 get_interface_mac_address(index, &(*duid)[2 + 2 + 4]);
509 duid_time = time(0) - DUID_TIME_EPOCH;
510 (*duid)[4] = duid_time >> 24;
511 (*duid)[5] = duid_time >> 16;
512 (*duid)[6] = duid_time >> 8;
513 (*duid)[7] = duid_time & 0xff;
515 case G_DHCPV6_DUID_EN:
517 case G_DHCPV6_DUID_LL:
518 *duid_len = 2 + 2 + ETH_ALEN;
519 *duid = g_try_malloc(*duid_len);
525 get_interface_mac_address(index, &(*duid)[2 + 2]);
534 int g_dhcpv6_client_set_duid(GDHCPClient *dhcp_client, unsigned char *duid,
537 if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
540 g_free(dhcp_client->duid);
542 dhcp_client->duid = duid;
543 dhcp_client->duid_len = duid_len;
548 int g_dhcpv6_client_set_oro(GDHCPClient *dhcp_client, int args, ...)
551 int i, j, len = sizeof(uint16_t) * args;
554 values = g_try_malloc(len);
559 for (i = 0, j = 0; i < args; i++) {
560 uint16_t value = va_arg(va, int);
561 values[j++] = value >> 8;
562 values[j++] = value & 0xff;
566 g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_ORO, values, len);
571 static int send_dhcpv6_msg(GDHCPClient *dhcp_client, int type, char *msg)
573 struct dhcpv6_packet *packet;
574 uint8_t buf[MAX_DHCPV6_PKT_SIZE];
578 memset(buf, 0, sizeof(buf));
579 packet = (struct dhcpv6_packet *)&buf[0];
580 ptr = buf + sizeof(struct dhcpv6_packet);
582 debug(dhcp_client, "sending DHCPv6 %s message", msg);
584 init_packet(dhcp_client, packet, type);
586 dhcp_client->xid = packet->transaction_id[0] << 16 |
587 packet->transaction_id[1] << 8 |
588 packet->transaction_id[2];
590 max_buf = MAX_DHCPV6_PKT_SIZE - sizeof(struct dhcpv6_packet);
592 add_dhcpv6_request_options(dhcp_client, packet, buf, max_buf, &ptr);
594 add_dhcpv6_send_options(dhcp_client, buf, max_buf, &ptr);
596 ret = dhcpv6_send_packet(dhcp_client->ifindex, packet, ptr - buf);
598 debug(dhcp_client, "sent %d pkt %p len %d", ret, packet, ptr - buf);
602 static int send_information_req(GDHCPClient *dhcp_client)
604 return send_dhcpv6_msg(dhcp_client, DHCPV6_INFORMATION_REQ,
608 static void remove_value(gpointer data, gpointer user_data)
614 static void remove_option_value(gpointer data)
616 GList *option_value = data;
618 g_list_foreach(option_value, remove_value, NULL);
621 GDHCPClient *g_dhcp_client_new(GDHCPType type,
622 int ifindex, GDHCPClientError *error)
624 GDHCPClient *dhcp_client;
627 *error = G_DHCP_CLIENT_ERROR_INVALID_INDEX;
631 dhcp_client = g_try_new0(GDHCPClient, 1);
632 if (dhcp_client == NULL) {
633 *error = G_DHCP_CLIENT_ERROR_NOMEM;
637 dhcp_client->interface = get_interface_name(ifindex);
638 if (dhcp_client->interface == NULL) {
639 *error = G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE;
643 if (interface_is_up(ifindex) == FALSE) {
644 *error = G_DHCP_CLIENT_ERROR_INTERFACE_DOWN;
648 get_interface_mac_address(ifindex, dhcp_client->mac_address);
650 dhcp_client->listener_sockfd = -1;
651 dhcp_client->listener_channel = NULL;
652 dhcp_client->listen_mode = L_NONE;
653 dhcp_client->ref_count = 1;
654 dhcp_client->type = type;
655 dhcp_client->ifindex = ifindex;
656 dhcp_client->lease_available_cb = NULL;
657 dhcp_client->ipv4ll_available_cb = NULL;
658 dhcp_client->no_lease_cb = NULL;
659 dhcp_client->lease_lost_cb = NULL;
660 dhcp_client->ipv4ll_lost_cb = NULL;
661 dhcp_client->address_conflict_cb = NULL;
662 dhcp_client->listener_watch = 0;
663 dhcp_client->retry_times = 0;
664 dhcp_client->ack_retry_times = 0;
665 dhcp_client->code_value_hash = g_hash_table_new_full(g_direct_hash,
666 g_direct_equal, NULL, remove_option_value);
667 dhcp_client->send_value_hash = g_hash_table_new_full(g_direct_hash,
668 g_direct_equal, NULL, g_free);
669 dhcp_client->request_list = NULL;
670 dhcp_client->require_list = NULL;
671 dhcp_client->duid = NULL;
672 dhcp_client->duid_len = 0;
674 *error = G_DHCP_CLIENT_ERROR_NONE;
679 g_free(dhcp_client->interface);
684 #define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68)
686 static int dhcp_l2_socket(int ifindex)
689 struct sockaddr_ll sock;
694 * I've selected not to see LL header, so BPF doesn't see it, too.
695 * The filter may also pass non-IP and non-ARP packets, but we do
696 * a more complete check when receiving the message in userspace.
698 * and filter shamelessly stolen from:
700 * http://www.flamewarmaster.de/software/dhcpclient/
702 * There are a few other interesting ideas on that page (look under
703 * "Motivation"). Use of netlink events is most interesting. Think
704 * of various network servers listening for events and reconfiguring.
705 * That would obsolete sending HUP signals and/or make use of restarts.
707 * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
710 * TODO: make conditional?
712 static const struct sock_filter filter_instr[] = {
714 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
715 /* L5, L1, is UDP? */
716 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0),
717 /* ugly check for arp on ethernet-like and IPv4 */
718 BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */
719 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4),/* L3, L4 */
721 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */
722 /* check udp source and destination ports */
723 BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
725 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),
727 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* L3: pass */
728 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
731 static const struct sock_fprog filter_prog = {
732 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
733 /* casting const away: */
734 .filter = (struct sock_filter *) filter_instr,
737 fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
741 if (SERVER_PORT == 67 && CLIENT_PORT == 68)
742 /* Use only if standard ports are in use */
743 setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
744 sizeof(filter_prog));
746 memset(&sock, 0, sizeof(sock));
747 sock.sll_family = AF_PACKET;
748 sock.sll_protocol = htons(ETH_P_IP);
749 sock.sll_ifindex = ifindex;
751 if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
759 static gboolean sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
761 if (packet->ip.protocol != IPPROTO_UDP)
764 if (packet->ip.version != IPVERSION)
767 if (packet->ip.ihl != sizeof(packet->ip) >> 2)
770 if (packet->udp.dest != htons(CLIENT_PORT))
773 if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip)))
779 static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
782 struct ip_udp_dhcp_packet packet;
785 memset(&packet, 0, sizeof(packet));
787 bytes = read(fd, &packet, sizeof(packet));
791 if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
794 if (bytes < ntohs(packet.ip.tot_len))
795 /* packet is bigger than sizeof(packet), we did partial read */
798 /* ignore any extra garbage bytes */
799 bytes = ntohs(packet.ip.tot_len);
801 if (sanity_check(&packet, bytes) == FALSE)
804 check = packet.ip.check;
806 if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip)))
809 /* verify UDP checksum. IP header has to be modified for this */
810 memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
811 /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
812 packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
813 check = packet.udp.check;
814 packet.udp.check = 0;
815 if (check && check != dhcp_checksum(&packet, bytes))
818 memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) +
819 sizeof(packet.udp)));
821 if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
824 return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
827 static void ipv4ll_start(GDHCPClient *dhcp_client)
832 if (dhcp_client->timeout > 0) {
833 g_source_remove(dhcp_client->timeout);
834 dhcp_client->timeout = 0;
837 switch_listening_mode(dhcp_client, L_NONE);
838 dhcp_client->type = G_DHCP_IPV4LL;
839 dhcp_client->retry_times = 0;
840 dhcp_client->requested_ip = 0;
842 /*try to start with a based mac address ip*/
843 seed = (dhcp_client->mac_address[4] << 8 | dhcp_client->mac_address[4]);
844 dhcp_client->requested_ip = ipv4ll_random_ip(seed);
846 /*first wait a random delay to avoid storm of arp request on boot*/
847 timeout = ipv4ll_random_delay_ms(PROBE_WAIT);
849 dhcp_client->retry_times++;
850 dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
857 static void ipv4ll_stop(GDHCPClient *dhcp_client)
860 switch_listening_mode(dhcp_client, L_NONE);
862 if (dhcp_client->timeout > 0)
863 g_source_remove(dhcp_client->timeout);
865 if (dhcp_client->listener_watch > 0) {
866 g_source_remove(dhcp_client->listener_watch);
867 dhcp_client->listener_watch = 0;
870 dhcp_client->state = IPV4LL_PROBE;
871 dhcp_client->retry_times = 0;
872 dhcp_client->requested_ip = 0;
874 g_free(dhcp_client->assigned_ip);
875 dhcp_client->assigned_ip = NULL;
878 static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
881 struct ether_arp arp;
882 uint32_t ip_requested;
886 memset(&arp, 0, sizeof(arp));
888 bytes = read(dhcp_client->listener_sockfd, &arp, sizeof(arp));
892 if (arp.arp_op != htons(ARPOP_REPLY) &&
893 arp.arp_op != htons(ARPOP_REQUEST))
896 ip_requested = ntohl(dhcp_client->requested_ip);
897 source_conflict = !memcmp(arp.arp_spa, &ip_requested,
898 sizeof(ip_requested));
900 target_conflict = !memcmp(arp.arp_tpa, &ip_requested,
901 sizeof(ip_requested));
903 if (!source_conflict && !target_conflict)
906 dhcp_client->conflicts++;
908 debug(dhcp_client, "IPV4LL conflict detected");
910 if (dhcp_client->state == IPV4LL_MONITOR) {
911 if (!source_conflict)
913 dhcp_client->state = IPV4LL_DEFEND;
914 debug(dhcp_client, "DEFEND mode conflicts : %d",
915 dhcp_client->conflicts);
916 /*Try to defend with a single announce*/
917 send_announce_packet(dhcp_client);
921 if (dhcp_client->state == IPV4LL_DEFEND) {
922 if (!source_conflict)
924 else if (dhcp_client->ipv4ll_lost_cb != NULL)
925 dhcp_client->ipv4ll_lost_cb(dhcp_client,
926 dhcp_client->ipv4ll_lost_data);
929 ipv4ll_stop(dhcp_client);
931 if (dhcp_client->conflicts < MAX_CONFLICTS) {
932 /*restart whole state machine*/
933 dhcp_client->retry_times++;
934 dhcp_client->timeout =
935 g_timeout_add_full(G_PRIORITY_HIGH,
936 ipv4ll_random_delay_ms(PROBE_WAIT),
941 /* Here we got a lot of conflicts, RFC3927 states that we have
942 * to wait RATE_LIMIT_INTERVAL before retrying,
943 * but we just report failure.
945 else if (dhcp_client->no_lease_cb != NULL)
946 dhcp_client->no_lease_cb(dhcp_client,
947 dhcp_client->no_lease_data);
952 static gboolean check_package_owner(GDHCPClient *dhcp_client, gpointer pkt)
954 if (dhcp_client->type == G_DHCP_IPV6) {
955 struct dhcpv6_packet *packet6 = pkt;
961 xid = packet6->transaction_id[0] << 16 |
962 packet6->transaction_id[1] << 8 |
963 packet6->transaction_id[2];
965 if (xid != dhcp_client->xid)
968 struct dhcp_packet *packet = pkt;
970 if (packet->xid != dhcp_client->xid)
973 if (packet->hlen != 6)
976 if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
983 static void start_request(GDHCPClient *dhcp_client);
985 static gboolean request_timeout(gpointer user_data)
987 GDHCPClient *dhcp_client = user_data;
989 debug(dhcp_client, "request timeout (retries %d)",
990 dhcp_client->retry_times);
992 dhcp_client->retry_times++;
994 start_request(dhcp_client);
999 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
1000 gpointer user_data);
1002 static int switch_listening_mode(GDHCPClient *dhcp_client,
1003 ListenMode listen_mode)
1005 GIOChannel *listener_channel;
1006 int listener_sockfd;
1008 if (dhcp_client->listen_mode == listen_mode)
1011 debug(dhcp_client, "switch listening mode (%d ==> %d)",
1012 dhcp_client->listen_mode, listen_mode);
1014 if (dhcp_client->listen_mode != L_NONE) {
1015 if (dhcp_client->listener_watch > 0)
1016 g_source_remove(dhcp_client->listener_watch);
1017 dhcp_client->listener_channel = NULL;
1018 dhcp_client->listen_mode = L_NONE;
1019 dhcp_client->listener_sockfd = -1;
1020 dhcp_client->listener_watch = 0;
1023 if (listen_mode == L_NONE)
1026 if (listen_mode == L2)
1027 listener_sockfd = dhcp_l2_socket(dhcp_client->ifindex);
1028 else if (listen_mode == L3) {
1029 if (dhcp_client->type == G_DHCP_IPV6)
1030 listener_sockfd = dhcp_l3_socket(DHCPV6_CLIENT_PORT,
1031 dhcp_client->interface,
1034 listener_sockfd = dhcp_l3_socket(CLIENT_PORT,
1035 dhcp_client->interface,
1037 } else if (listen_mode == L_ARP)
1038 listener_sockfd = ipv4ll_arp_socket(dhcp_client->ifindex);
1042 if (listener_sockfd < 0)
1045 listener_channel = g_io_channel_unix_new(listener_sockfd);
1046 if (listener_channel == NULL) {
1047 /* Failed to create listener channel */
1048 close(listener_sockfd);
1052 dhcp_client->listen_mode = listen_mode;
1053 dhcp_client->listener_sockfd = listener_sockfd;
1054 dhcp_client->listener_channel = listener_channel;
1056 g_io_channel_set_close_on_unref(listener_channel, TRUE);
1057 dhcp_client->listener_watch =
1058 g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH,
1059 G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
1060 listener_event, dhcp_client,
1062 g_io_channel_unref(dhcp_client->listener_channel);
1067 static void start_request(GDHCPClient *dhcp_client)
1069 debug(dhcp_client, "start request (retries %d)",
1070 dhcp_client->retry_times);
1072 if (dhcp_client->retry_times == REQUEST_RETRIES) {
1073 dhcp_client->state = INIT_SELECTING;
1074 ipv4ll_start(dhcp_client);
1079 if (dhcp_client->retry_times == 0) {
1080 dhcp_client->state = REQUESTING;
1081 switch_listening_mode(dhcp_client, L2);
1084 send_select(dhcp_client);
1086 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1093 static uint32_t get_lease(struct dhcp_packet *packet)
1096 uint32_t lease_seconds;
1098 option_u8 = dhcp_get_option(packet, DHCP_LEASE_TIME);
1099 if (option_u8 == NULL)
1102 lease_seconds = dhcp_get_unaligned((uint32_t *) option_u8);
1103 lease_seconds = ntohl(lease_seconds);
1104 /* paranoia: must not be prone to overflows */
1105 lease_seconds &= 0x0fffffff;
1106 if (lease_seconds < 10)
1109 return lease_seconds;
1112 static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times)
1114 debug(dhcp_client, "restart DHCP (retries %d)", retry_times);
1116 if (dhcp_client->timeout > 0) {
1117 g_source_remove(dhcp_client->timeout);
1118 dhcp_client->timeout = 0;
1121 dhcp_client->retry_times = retry_times;
1122 dhcp_client->requested_ip = 0;
1123 switch_listening_mode(dhcp_client, L2);
1125 g_dhcp_client_start(dhcp_client, dhcp_client->last_address);
1128 static gboolean start_rebound_timeout(gpointer user_data)
1130 GDHCPClient *dhcp_client = user_data;
1132 debug(dhcp_client, "start rebound timeout");
1134 switch_listening_mode(dhcp_client, L2);
1136 dhcp_client->lease_seconds >>= 1;
1138 /* We need to have enough time to receive ACK package*/
1139 if (dhcp_client->lease_seconds <= 6) {
1141 /* ip need to be cleared */
1142 if (dhcp_client->lease_lost_cb != NULL)
1143 dhcp_client->lease_lost_cb(dhcp_client,
1144 dhcp_client->lease_lost_data);
1146 restart_dhcp(dhcp_client, 0);
1148 send_rebound(dhcp_client);
1150 dhcp_client->timeout =
1151 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1152 dhcp_client->lease_seconds >> 1,
1153 start_rebound_timeout,
1161 static void start_rebound(GDHCPClient *dhcp_client)
1163 debug(dhcp_client, "start rebound");
1165 dhcp_client->state = REBINDING;
1167 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1168 dhcp_client->lease_seconds >> 1,
1169 start_rebound_timeout,
1174 static gboolean start_renew_timeout(gpointer user_data)
1176 GDHCPClient *dhcp_client = user_data;
1178 debug(dhcp_client, "start renew timeout");
1180 dhcp_client->state = RENEWING;
1182 dhcp_client->lease_seconds >>= 1;
1184 switch_listening_mode(dhcp_client, L3);
1185 if (dhcp_client->lease_seconds <= 60)
1186 start_rebound(dhcp_client);
1188 send_renew(dhcp_client);
1190 if (dhcp_client->timeout > 0)
1191 g_source_remove(dhcp_client->timeout);
1193 dhcp_client->timeout =
1194 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1195 dhcp_client->lease_seconds >> 1,
1196 start_renew_timeout,
1204 static void start_bound(GDHCPClient *dhcp_client)
1206 debug(dhcp_client, "start bound");
1208 dhcp_client->state = BOUND;
1210 if (dhcp_client->timeout > 0)
1211 g_source_remove(dhcp_client->timeout);
1213 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1214 dhcp_client->lease_seconds >> 1,
1215 start_renew_timeout, dhcp_client,
1219 static gboolean restart_dhcp_timeout(gpointer user_data)
1221 GDHCPClient *dhcp_client = user_data;
1223 debug(dhcp_client, "restart DHCP timeout");
1225 dhcp_client->ack_retry_times++;
1227 restart_dhcp(dhcp_client, dhcp_client->ack_retry_times);
1232 static char *get_ip(uint32_t ip)
1234 struct in_addr addr;
1238 return g_strdup(inet_ntoa(addr));
1241 /* get a rough idea of how long an option will be */
1242 static const uint8_t len_of_option_as_string[] = {
1243 [OPTION_IP] = sizeof("255.255.255.255 "),
1244 [OPTION_STRING] = 1,
1245 [OPTION_U8] = sizeof("255 "),
1246 [OPTION_U16] = sizeof("65535 "),
1247 [OPTION_U32] = sizeof("4294967295 "),
1250 static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
1252 return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
1255 /* Create "opt_value1 option_value2 ..." string */
1256 static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
1258 unsigned upper_length;
1262 len = option[OPT_LEN - OPT_DATA];
1263 type &= OPTION_TYPE_MASK;
1264 optlen = dhcp_option_lengths[type];
1267 upper_length = len_of_option_as_string[type] *
1268 ((unsigned)len / (unsigned)optlen);
1269 dest = ret = malloc(upper_length + 1);
1273 while (len >= optlen) {
1276 dest += sprint_nip(dest, "", option);
1279 uint16_t val_u16 = dhcp_get_unaligned(
1280 (uint16_t *) option);
1281 dest += sprintf(dest, "%u", ntohs(val_u16));
1285 uint32_t val_u32 = dhcp_get_unaligned(
1286 (uint32_t *) option);
1287 dest += sprintf(dest, type == OPTION_U32 ? "%lu" :
1288 "%ld", (unsigned long) ntohl(val_u32));
1292 memcpy(dest, option, len);
1309 static GList *get_option_value_list(char *value, GDHCPOptionType type)
1317 if (type == OPTION_STRING)
1318 return g_list_append(list, g_strdup(value));
1320 while ((pos = strchr(pos, ' ')) != NULL) {
1323 list = g_list_append(list, g_strdup(value));
1328 list = g_list_append(list, g_strdup(value));
1333 static GList *get_dhcpv6_option_value_list(GDHCPClient *dhcp_client,
1335 unsigned char *value)
1347 static void get_dhcpv6_request(GDHCPClient *dhcp_client,
1348 struct dhcpv6_packet *packet,
1351 GList *list, *value_list;
1354 uint16_t option_len;
1356 for (list = dhcp_client->request_list; list; list = list->next) {
1357 code = (uint16_t) GPOINTER_TO_INT(list->data);
1359 option = dhcpv6_get_option(packet, pkt_len, code, &option_len,
1361 if (option == NULL) {
1362 g_hash_table_remove(dhcp_client->code_value_hash,
1363 GINT_TO_POINTER((int) code));
1367 value_list = get_dhcpv6_option_value_list(dhcp_client, code,
1368 option_len, option);
1370 debug(dhcp_client, "code %d %p len %d list %p", code, option,
1371 option_len, value_list);
1373 if (value_list == NULL)
1374 g_hash_table_remove(dhcp_client->code_value_hash,
1375 GINT_TO_POINTER((int) code));
1377 g_hash_table_insert(dhcp_client->code_value_hash,
1378 GINT_TO_POINTER((int) code), value_list);
1382 static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
1384 GDHCPOptionType type;
1385 GList *list, *value_list;
1390 for (list = dhcp_client->request_list; list; list = list->next) {
1391 code = (uint8_t) GPOINTER_TO_INT(list->data);
1393 option = dhcp_get_option(packet, code);
1394 if (option == NULL) {
1395 g_hash_table_remove(dhcp_client->code_value_hash,
1396 GINT_TO_POINTER((int) code));
1400 type = dhcp_get_code_type(code);
1402 option_value = malloc_option_value_string(option, type);
1403 if (option_value == NULL)
1404 g_hash_table_remove(dhcp_client->code_value_hash,
1405 GINT_TO_POINTER((int) code));
1407 value_list = get_option_value_list(option_value, type);
1409 g_free(option_value);
1411 if (value_list == NULL)
1412 g_hash_table_remove(dhcp_client->code_value_hash,
1413 GINT_TO_POINTER((int) code));
1415 g_hash_table_insert(dhcp_client->code_value_hash,
1416 GINT_TO_POINTER((int) code), value_list);
1420 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
1423 GDHCPClient *dhcp_client = user_data;
1424 struct dhcp_packet packet;
1425 struct dhcpv6_packet *packet6 = NULL;
1426 uint8_t *message_type = NULL, *client_id = NULL, *option_u8,
1428 uint16_t option_len = 0, status = 0;
1430 unsigned char buf[MAX_DHCPV6_PKT_SIZE];
1431 uint16_t pkt_len = 0;
1435 if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
1436 dhcp_client->listener_watch = 0;
1440 if (dhcp_client->listen_mode == L_NONE)
1445 if (dhcp_client->listen_mode == L2)
1446 re = dhcp_recv_l2_packet(&packet,
1447 dhcp_client->listener_sockfd);
1448 else if (dhcp_client->listen_mode == L3) {
1449 if (dhcp_client->type == G_DHCP_IPV6) {
1450 re = dhcpv6_recv_l3_packet(&packet6, buf, sizeof(buf),
1451 dhcp_client->listener_sockfd);
1455 re = dhcp_recv_l3_packet(&packet,
1456 dhcp_client->listener_sockfd);
1457 } else if (dhcp_client->listen_mode == L_ARP) {
1458 re = ipv4ll_recv_arp_packet(dhcp_client);
1467 if (check_package_owner(dhcp_client, pkt) == FALSE)
1470 if (dhcp_client->type == G_DHCP_IPV6) {
1472 client_id = dhcpv6_get_option(packet6, pkt_len,
1473 G_DHCPV6_CLIENTID, &option_len, &count);
1475 if (client_id == NULL || count == 0 || option_len == 0 ||
1476 memcmp(dhcp_client->duid, client_id,
1477 dhcp_client->duid_len) != 0) {
1479 "client duid error, discarding msg %p/%d/%d",
1480 client_id, option_len, count);
1484 option_u8 = dhcpv6_get_option(packet6, pkt_len,
1485 G_DHCPV6_STATUS_CODE, &option_len, NULL);
1486 if (option_u8 != 0 && option_len > 0) {
1487 status = option_u8[0]<<8 | option_u8[1];
1489 gchar *txt = g_strndup((gchar *)&option_u8[2],
1491 debug(dhcp_client, "error code %d: %s",
1495 dhcp_client->status_code = status;
1497 dhcp_client->status_code = 0;
1500 message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
1502 if (message_type == NULL && client_id == NULL)
1503 /* No message type / client id option, ignore package */
1506 debug(dhcp_client, "received DHCP packet (current state %d)",
1507 dhcp_client->state);
1509 switch (dhcp_client->state) {
1510 case INIT_SELECTING:
1511 if (*message_type != DHCPOFFER)
1514 g_source_remove(dhcp_client->timeout);
1515 dhcp_client->timeout = 0;
1516 dhcp_client->retry_times = 0;
1518 option_u8 = dhcp_get_option(&packet, DHCP_SERVER_ID);
1519 dhcp_client->server_ip =
1520 dhcp_get_unaligned((uint32_t *) option_u8);
1521 dhcp_client->requested_ip = packet.yiaddr;
1523 dhcp_client->state = REQUESTING;
1525 start_request(dhcp_client);
1531 if (*message_type == DHCPACK) {
1532 dhcp_client->retry_times = 0;
1534 if (dhcp_client->timeout > 0)
1535 g_source_remove(dhcp_client->timeout);
1536 dhcp_client->timeout = 0;
1538 dhcp_client->lease_seconds = get_lease(&packet);
1540 get_request(dhcp_client, &packet);
1542 switch_listening_mode(dhcp_client, L_NONE);
1544 g_free(dhcp_client->assigned_ip);
1545 dhcp_client->assigned_ip = get_ip(packet.yiaddr);
1547 /* Address should be set up here */
1548 if (dhcp_client->lease_available_cb != NULL)
1549 dhcp_client->lease_available_cb(dhcp_client,
1550 dhcp_client->lease_available_data);
1552 start_bound(dhcp_client);
1553 } else if (*message_type == DHCPNAK) {
1554 dhcp_client->retry_times = 0;
1556 if (dhcp_client->timeout > 0)
1557 g_source_remove(dhcp_client->timeout);
1559 dhcp_client->timeout = g_timeout_add_seconds_full(
1561 restart_dhcp_timeout,
1567 case INFORMATION_REQ:
1568 if (dhcp_client->type != G_DHCP_IPV6)
1571 if (packet6->message != DHCPV6_REPLY)
1576 server_id = dhcpv6_get_option(packet6, pkt_len,
1577 G_DHCPV6_SERVERID, &option_len, &count);
1578 if (server_id == NULL || count != 1 || option_len == 0) {
1579 /* RFC 3315, 15.10 */
1581 "server duid error, discarding msg %p/%d/%d",
1582 server_id, option_len, count);
1586 switch_listening_mode(dhcp_client, L_NONE);
1587 get_dhcpv6_request(dhcp_client, packet6, pkt_len);
1589 if (dhcp_client->information_req_cb != NULL) {
1591 * The dhcp_client might not be valid after the
1592 * callback call so just return immediately.
1594 dhcp_client->information_req_cb(dhcp_client,
1595 dhcp_client->information_req_data);
1603 debug(dhcp_client, "processed DHCP packet (new state %d)",
1604 dhcp_client->state);
1609 static gboolean discover_timeout(gpointer user_data)
1611 GDHCPClient *dhcp_client = user_data;
1613 dhcp_client->retry_times++;
1616 * We do not send the REQUESTED IP option if we are retrying because
1617 * if the server is non-authoritative it will ignore the request if the
1618 * option is present.
1620 g_dhcp_client_start(dhcp_client, NULL);
1625 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data)
1627 GDHCPClient *dhcp_client = dhcp_data;
1629 debug(dhcp_client, "back to MONITOR mode");
1631 dhcp_client->conflicts = 0;
1632 dhcp_client->state = IPV4LL_MONITOR;
1637 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data)
1639 GDHCPClient *dhcp_client = dhcp_data;
1642 debug(dhcp_client, "request timeout (retries %d)",
1643 dhcp_client->retry_times);
1645 if (dhcp_client->retry_times != ANNOUNCE_NUM){
1646 dhcp_client->retry_times++;
1647 send_announce_packet(dhcp_client);
1651 ip = htonl(dhcp_client->requested_ip);
1652 debug(dhcp_client, "switching to monitor mode");
1653 dhcp_client->state = IPV4LL_MONITOR;
1654 dhcp_client->assigned_ip = get_ip(ip);
1656 if (dhcp_client->ipv4ll_available_cb != NULL)
1657 dhcp_client->ipv4ll_available_cb(dhcp_client,
1658 dhcp_client->ipv4ll_available_data);
1659 dhcp_client->conflicts = 0;
1664 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data)
1667 GDHCPClient *dhcp_client = dhcp_data;
1669 debug(dhcp_client, "IPV4LL probe timeout (retries %d)",
1670 dhcp_client->retry_times);
1672 if (dhcp_client->retry_times == PROBE_NUM) {
1673 dhcp_client->state = IPV4LL_ANNOUNCE;
1674 dhcp_client->retry_times = 0;
1676 dhcp_client->retry_times++;
1677 send_announce_packet(dhcp_client);
1680 dhcp_client->retry_times++;
1681 send_probe_packet(dhcp_client);
1686 int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
1691 if (dhcp_client->type == G_DHCP_IPV6) {
1692 if (dhcp_client->information_req_cb) {
1693 dhcp_client->state = INFORMATION_REQ;
1694 re = switch_listening_mode(dhcp_client, L3);
1696 switch_listening_mode(dhcp_client, L_NONE);
1697 dhcp_client->state = 0;
1700 send_information_req(dhcp_client);
1705 if (dhcp_client->retry_times == DISCOVER_RETRIES) {
1706 ipv4ll_start(dhcp_client);
1710 if (dhcp_client->retry_times == 0) {
1711 g_free(dhcp_client->assigned_ip);
1712 dhcp_client->assigned_ip = NULL;
1714 dhcp_client->state = INIT_SELECTING;
1715 re = switch_listening_mode(dhcp_client, L2);
1719 dhcp_client->xid = rand();
1722 if (last_address == NULL) {
1725 addr = inet_addr(last_address);
1726 if (addr == 0xFFFFFFFF) {
1729 g_free(dhcp_client->last_address);
1730 dhcp_client->last_address = g_strdup(last_address);
1733 send_discover(dhcp_client, addr);
1735 dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1743 void g_dhcp_client_stop(GDHCPClient *dhcp_client)
1745 switch_listening_mode(dhcp_client, L_NONE);
1747 if (dhcp_client->state == BOUND ||
1748 dhcp_client->state == RENEWING ||
1749 dhcp_client->state == REBINDING)
1750 send_release(dhcp_client, dhcp_client->server_ip,
1751 dhcp_client->requested_ip);
1753 if (dhcp_client->timeout > 0) {
1754 g_source_remove(dhcp_client->timeout);
1755 dhcp_client->timeout = 0;
1758 if (dhcp_client->listener_watch > 0) {
1759 g_source_remove(dhcp_client->listener_watch);
1760 dhcp_client->listener_watch = 0;
1763 dhcp_client->listener_channel = NULL;
1765 dhcp_client->retry_times = 0;
1766 dhcp_client->ack_retry_times = 0;
1768 dhcp_client->requested_ip = 0;
1769 dhcp_client->state = RELEASED;
1770 dhcp_client->lease_seconds = 0;
1773 GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
1774 unsigned char option_code)
1776 return g_hash_table_lookup(dhcp_client->code_value_hash,
1777 GINT_TO_POINTER((int) option_code));
1780 void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
1781 GDHCPClientEvent event,
1782 GDHCPClientEventFunc func,
1786 case G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE:
1787 dhcp_client->lease_available_cb = func;
1788 dhcp_client->lease_available_data = data;
1790 case G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE:
1791 if (dhcp_client->type == G_DHCP_IPV6)
1793 dhcp_client->ipv4ll_available_cb = func;
1794 dhcp_client->ipv4ll_available_data = data;
1796 case G_DHCP_CLIENT_EVENT_NO_LEASE:
1797 dhcp_client->no_lease_cb = func;
1798 dhcp_client->no_lease_data = data;
1800 case G_DHCP_CLIENT_EVENT_LEASE_LOST:
1801 dhcp_client->lease_lost_cb = func;
1802 dhcp_client->lease_lost_data = data;
1804 case G_DHCP_CLIENT_EVENT_IPV4LL_LOST:
1805 if (dhcp_client->type == G_DHCP_IPV6)
1807 dhcp_client->ipv4ll_lost_cb = func;
1808 dhcp_client->ipv4ll_lost_data = data;
1810 case G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT:
1811 dhcp_client->address_conflict_cb = func;
1812 dhcp_client->address_conflict_data = data;
1814 case G_DHCP_CLIENT_EVENT_INFORMATION_REQ:
1815 if (dhcp_client->type == G_DHCP_IPV4)
1817 dhcp_client->information_req_cb = func;
1818 dhcp_client->information_req_data = data;
1823 int g_dhcp_client_get_index(GDHCPClient *dhcp_client)
1825 return dhcp_client->ifindex;
1828 char *g_dhcp_client_get_address(GDHCPClient *dhcp_client)
1830 return g_strdup(dhcp_client->assigned_ip);
1833 char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
1835 GList *option = NULL;
1837 if (dhcp_client->type == G_DHCP_IPV6)
1840 switch (dhcp_client->state) {
1842 case IPV4LL_MONITOR:
1843 return g_strdup("255.255.0.0");
1847 option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET);
1849 return g_strdup(option->data);
1850 case INIT_SELECTING:
1854 case IPV4LL_ANNOUNCE:
1855 case INFORMATION_REQ:
1861 GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
1862 unsigned int option_code)
1864 if (g_list_find(dhcp_client->request_list,
1865 GINT_TO_POINTER((int) option_code)) == NULL)
1866 dhcp_client->request_list = g_list_prepend(
1867 dhcp_client->request_list,
1868 (GINT_TO_POINTER((int) option_code)));
1870 return G_DHCP_CLIENT_ERROR_NONE;
1873 static uint8_t *alloc_dhcp_option(int code, const char *str, int extra)
1876 int len = strnlen(str, 255);
1878 storage = malloc(len + extra + OPT_DATA);
1879 storage[OPT_CODE] = code;
1880 storage[OPT_LEN] = len + extra;
1881 memcpy(storage + extra + OPT_DATA, str, len);
1886 /* Now only support send hostname */
1887 GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
1888 unsigned char option_code, const char *option_value)
1890 uint8_t *binary_option;
1892 if (option_code == G_DHCP_HOST_NAME && option_value != NULL) {
1893 binary_option = alloc_dhcp_option(option_code,
1896 g_hash_table_insert(dhcp_client->send_value_hash,
1897 GINT_TO_POINTER((int) option_code), binary_option);
1900 return G_DHCP_CLIENT_ERROR_NONE;
1903 static uint8_t *alloc_dhcpv6_option(uint16_t code, uint8_t *option,
1908 storage = g_malloc(2 + 2 + len);
1909 if (storage == NULL)
1912 storage[0] = code >> 8;
1913 storage[1] = code & 0xff;
1914 storage[2] = len >> 8;
1915 storage[3] = len & 0xff;
1916 memcpy(storage + 2 + 2, option, len);
1921 void g_dhcpv6_client_set_send(GDHCPClient *dhcp_client,
1922 uint16_t option_code,
1923 uint8_t *option_value,
1924 uint16_t option_len)
1926 if (option_value != NULL) {
1927 uint8_t *binary_option;
1929 debug(dhcp_client, "setting option %d to %p len %d",
1930 option_code, option_value, option_len);
1932 binary_option = alloc_dhcpv6_option(option_code, option_value,
1934 if (binary_option != NULL)
1935 g_hash_table_insert(dhcp_client->send_value_hash,
1936 GINT_TO_POINTER((int) option_code),
1941 uint16_t g_dhcpv6_client_get_status(GDHCPClient *dhcp_client)
1943 if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
1946 return dhcp_client->status_code;
1949 GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client)
1951 if (dhcp_client == NULL)
1954 __sync_fetch_and_add(&dhcp_client->ref_count, 1);
1959 void g_dhcp_client_unref(GDHCPClient *dhcp_client)
1961 if (dhcp_client == NULL)
1964 if (__sync_fetch_and_sub(&dhcp_client->ref_count, 1) != 1)
1967 g_dhcp_client_stop(dhcp_client);
1969 g_free(dhcp_client->interface);
1970 g_free(dhcp_client->assigned_ip);
1971 g_free(dhcp_client->last_address);
1972 g_free(dhcp_client->duid);
1974 g_list_free(dhcp_client->request_list);
1975 g_list_free(dhcp_client->require_list);
1977 g_hash_table_destroy(dhcp_client->code_value_hash);
1978 g_hash_table_destroy(dhcp_client->send_value_hash);
1980 g_free(dhcp_client);
1983 void g_dhcp_client_set_debug(GDHCPClient *dhcp_client,
1984 GDHCPDebugFunc func, gpointer user_data)
1986 if (dhcp_client == NULL)
1989 dhcp_client->debug_func = func;
1990 dhcp_client->debug_data = user_data;