gdhcp: Add IPv4 Link-Local support
[framework/connectivity/connman.git] / gdhcp / client.c
1 /*
2  *
3  *  DHCP client library with GLib integration
4  *
5  *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
6  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/ioctl.h>
32 #include <arpa/inet.h>
33
34 #include <netpacket/packet.h>
35 #include <netinet/if_ether.h>
36 #include <net/ethernet.h>
37
38 #include <linux/if.h>
39 #include <linux/filter.h>
40
41 #include <glib.h>
42
43 #include "gdhcp.h"
44 #include "common.h"
45 #include "ipv4ll.h"
46
47 #define DISCOVER_TIMEOUT 3
48 #define DISCOVER_RETRIES 10
49
50 #define REQUEST_TIMEOUT 3
51 #define REQUEST_RETRIES 5
52
53 typedef enum _listen_mode {
54         L_NONE,
55         L2,
56         L3,
57         L_ARP,
58 } ListenMode;
59
60 typedef enum _dhcp_client_state {
61         INIT_SELECTING,
62         REQUESTING,
63         BOUND,
64         RENEWING,
65         REBINDING,
66         RELEASED,
67         IPV4LL_PROBE,
68         IPV4LL_ANNOUNCE,
69         IPV4LL_MONITOR,
70         IPV4LL_DEFEND,
71 } ClientState;
72
73 struct _GDHCPClient {
74         gint ref_count;
75         GDHCPType type;
76         ClientState state;
77         int ifindex;
78         char *interface;
79         uint8_t mac_address[6];
80         uint32_t xid;
81         uint32_t server_ip;
82         uint32_t requested_ip;
83         char *assigned_ip;
84         uint32_t lease_seconds;
85         ListenMode listen_mode;
86         int listener_sockfd;
87         uint8_t retry_times;
88         uint8_t ack_retry_times;
89         uint8_t conflicts;
90         guint timeout;
91         guint listener_watch;
92         GIOChannel *listener_channel;
93         GList *require_list;
94         GList *request_list;
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;
110         gpointer debug_data;
111 };
112
113 static inline void debug(GDHCPClient *client, const char *format, ...)
114 {
115         char str[256];
116         va_list ap;
117
118         if (client->debug_func == NULL)
119                 return;
120
121         va_start(ap, format);
122
123         if (vsnprintf(str, sizeof(str), format, ap) > 0)
124                 client->debug_func(str, client->debug_data);
125
126         va_end(ap);
127 }
128
129 /* Initialize the packet with the proper defaults */
130 static void init_packet(GDHCPClient *dhcp_client,
131                 struct dhcp_packet *packet, char type)
132 {
133         dhcp_init_header(packet, type);
134
135         memcpy(packet->chaddr, dhcp_client->mac_address, 6);
136 }
137
138 static void add_request_options(GDHCPClient *dhcp_client,
139                                 struct dhcp_packet *packet)
140 {
141         int len = 0;
142         GList *list;
143         uint8_t code;
144         int end = dhcp_end_option(packet->options);
145
146         for (list = dhcp_client->request_list; list; list = list->next) {
147                 code = (uint8_t) GPOINTER_TO_INT(list->data);
148
149                 packet->options[end + OPT_DATA + len] = code;
150                 len++;
151         }
152
153         if (len) {
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;
157         }
158 }
159
160 static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
161 {
162         uint8_t *option = value;
163         struct dhcp_packet *packet = user_data;
164
165         dhcp_add_binary_option(packet, option);
166 }
167
168 static void add_send_options(GDHCPClient *dhcp_client,
169                                 struct dhcp_packet *packet)
170 {
171         g_hash_table_foreach(dhcp_client->send_value_hash,
172                                 add_binary_option, packet);
173 }
174
175 static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
176 {
177         struct dhcp_packet packet;
178
179         debug(dhcp_client, "sending DHCP discover request");
180
181         init_packet(dhcp_client, &packet, DHCPDISCOVER);
182
183         packet.xid = dhcp_client->xid;
184
185         if (requested)
186                 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
187
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));
191
192         add_request_options(dhcp_client, &packet);
193
194         add_send_options(dhcp_client, &packet);
195
196         return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
197                                         INADDR_BROADCAST, SERVER_PORT,
198                                         MAC_BCAST_ADDR, dhcp_client->ifindex);
199 }
200
201 static int send_select(GDHCPClient *dhcp_client)
202 {
203         struct dhcp_packet packet;
204         struct in_addr addr;
205
206         debug(dhcp_client, "sending DHCP select request");
207
208         init_packet(dhcp_client, &packet, DHCPREQUEST);
209
210         packet.xid = dhcp_client->xid;
211
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);
215
216         add_request_options(dhcp_client, &packet);
217
218         add_send_options(dhcp_client, &packet);
219
220         addr.s_addr = dhcp_client->requested_ip;
221
222         return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
223                                         INADDR_BROADCAST, SERVER_PORT,
224                                         MAC_BCAST_ADDR, dhcp_client->ifindex);
225 }
226
227 static int send_renew(GDHCPClient *dhcp_client)
228 {
229         struct dhcp_packet packet;
230
231         debug(dhcp_client, "sending DHCP renew request");
232
233         init_packet(dhcp_client , &packet, DHCPREQUEST);
234         packet.xid = dhcp_client->xid;
235         packet.ciaddr = dhcp_client->requested_ip;
236
237         add_request_options(dhcp_client, &packet);
238
239         add_send_options(dhcp_client, &packet);
240
241         return dhcp_send_kernel_packet(&packet,
242                 dhcp_client->requested_ip, CLIENT_PORT,
243                 dhcp_client->server_ip, SERVER_PORT);
244 }
245
246 static int send_rebound(GDHCPClient *dhcp_client)
247 {
248         struct dhcp_packet packet;
249
250         debug(dhcp_client, "sending DHCP rebound request");
251
252         init_packet(dhcp_client , &packet, DHCPREQUEST);
253         packet.xid = dhcp_client->xid;
254         packet.ciaddr = dhcp_client->requested_ip;
255
256         add_request_options(dhcp_client, &packet);
257
258         add_send_options(dhcp_client, &packet);
259
260         return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
261                                         INADDR_BROADCAST, SERVER_PORT,
262                                         MAC_BCAST_ADDR, dhcp_client->ifindex);
263 }
264
265 static int send_release(GDHCPClient *dhcp_client,
266                         uint32_t server, uint32_t ciaddr)
267 {
268         struct dhcp_packet packet;
269
270         debug(dhcp_client, "sending DHCP release request");
271
272         init_packet(dhcp_client, &packet, DHCPRELEASE);
273         packet.xid = rand();
274         packet.ciaddr = ciaddr;
275
276         dhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
277
278         return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
279                                                 server, SERVER_PORT);
280 }
281
282 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data);
283 static int switch_listening_mode(GDHCPClient *dhcp_client,
284                                         ListenMode listen_mode);
285
286 static gboolean send_probe_packet(gpointer dhcp_data)
287 {
288         GDHCPClient *dhcp_client;
289         guint timeout;
290
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);
296         }
297
298         debug(dhcp_client, "sending IPV4LL probe request");
299
300         if (dhcp_client->retry_times == 1) {
301                 dhcp_client->state = IPV4LL_PROBE;
302                 switch_listening_mode(dhcp_client, L_ARP);
303         }
304         ipv4ll_send_arp_packet(dhcp_client->mac_address, 0,
305                         dhcp_client->requested_ip, dhcp_client->ifindex);
306
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;
311         } else
312                 timeout = (ANNOUNCE_WAIT * 1000);
313
314         dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
315                                                  timeout,
316                                                  ipv4ll_probe_timeout,
317                                                  dhcp_client,
318                                                  NULL);
319         return FALSE;
320 }
321
322 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data);
323 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data);
324
325 static gboolean send_announce_packet(gpointer dhcp_data)
326 {
327         GDHCPClient *dhcp_client;
328
329         dhcp_client = dhcp_data;
330
331         debug(dhcp_client, "sending IPV4LL announce request");
332
333         ipv4ll_send_arp_packet(dhcp_client->mac_address,
334                                 dhcp_client->requested_ip,
335                                 dhcp_client->requested_ip,
336                                 dhcp_client->ifindex);
337
338         if (dhcp_client->timeout > 0)
339                 g_source_remove(dhcp_client->timeout);
340         dhcp_client->timeout = 0;
341
342         if (dhcp_client->state == IPV4LL_DEFEND) {
343                 dhcp_client->timeout =
344                         g_timeout_add_seconds_full(G_PRIORITY_HIGH,
345                                                 DEFEND_INTERVAL,
346                                                 ipv4ll_defend_timeout,
347                                                 dhcp_client,
348                                                 NULL);
349                 return TRUE;
350         } else
351                 dhcp_client->timeout =
352                         g_timeout_add_seconds_full(G_PRIORITY_HIGH,
353                                                 ANNOUNCE_INTERVAL,
354                                                 ipv4ll_announce_timeout,
355                                                 dhcp_client,
356                                                 NULL);
357         return TRUE;
358 }
359
360 static void get_interface_mac_address(int index, uint8_t *mac_address)
361 {
362         struct ifreq ifr;
363         int sk, err;
364
365         sk = socket(PF_INET, SOCK_DGRAM, 0);
366         if (sk < 0) {
367                 perror("Open socket error");
368                 return;
369         }
370
371         memset(&ifr, 0, sizeof(ifr));
372         ifr.ifr_ifindex = index;
373
374         err = ioctl(sk, SIOCGIFNAME, &ifr);
375         if (err < 0) {
376                 perror("Get interface name error");
377                 goto done;
378         }
379
380         err = ioctl(sk, SIOCGIFHWADDR, &ifr);
381         if (err < 0) {
382                 perror("Get mac address error");
383                 goto done;
384         }
385
386         memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
387
388 done:
389         close(sk);
390 }
391
392 static void remove_value(gpointer data, gpointer user_data)
393 {
394         char *value = data;
395         g_free(value);
396 }
397
398 static void remove_option_value(gpointer data)
399 {
400         GList *option_value = data;
401
402         g_list_foreach(option_value, remove_value, NULL);
403 }
404
405 GDHCPClient *g_dhcp_client_new(GDHCPType type,
406                         int ifindex, GDHCPClientError *error)
407 {
408         GDHCPClient *dhcp_client;
409
410         if (ifindex < 0) {
411                 *error = G_DHCP_CLIENT_ERROR_INVALID_INDEX;
412                 return NULL;
413         }
414
415         dhcp_client = g_try_new0(GDHCPClient, 1);
416         if (dhcp_client == NULL) {
417                 *error = G_DHCP_CLIENT_ERROR_NOMEM;
418                 return NULL;
419         }
420
421         dhcp_client->interface = get_interface_name(ifindex);
422         if (dhcp_client->interface == NULL) {
423                 *error = G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE;
424                 goto error;
425         }
426
427         if (interface_is_up(ifindex) == FALSE) {
428                 *error = G_DHCP_CLIENT_ERROR_INTERFACE_DOWN;
429                 goto error;
430         }
431
432         get_interface_mac_address(ifindex, dhcp_client->mac_address);
433
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;
455
456         *error = G_DHCP_CLIENT_ERROR_NONE;
457
458         return dhcp_client;
459
460 error:
461         g_free(dhcp_client->interface);
462         g_free(dhcp_client);
463         return NULL;
464 }
465
466 #define SERVER_AND_CLIENT_PORTS  ((67 << 16) + 68)
467
468 static int dhcp_l2_socket(int ifindex)
469 {
470         int fd;
471         struct sockaddr_ll sock;
472
473         /*
474          * Comment:
475          *
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.
479          *
480          * and filter shamelessly stolen from:
481          *
482          *      http://www.flamewarmaster.de/software/dhcpclient/
483          *
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.
488          *
489          * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
490          * License: GPL v2.
491          *
492          * TODO: make conditional?
493          */
494         static const struct sock_filter filter_instr[] = {
495                 /* check for udp */
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 */
502                 /* skip IP header */
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),
506                 /* L3, L4 */
507                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),
508                 /* returns */
509                 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* L3: pass */
510                 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
511         };
512
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,
517         };
518
519         fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
520         if (fd < 0)
521                 return fd;
522
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));
527
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;
532
533         if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
534                 close(fd);
535                 return -errno;
536         }
537
538         return fd;
539 }
540
541 static gboolean sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
542 {
543         if (packet->ip.protocol != IPPROTO_UDP)
544                 return FALSE;
545
546         if (packet->ip.version != IPVERSION)
547                 return FALSE;
548
549         if (packet->ip.ihl != sizeof(packet->ip) >> 2)
550                 return FALSE;
551
552         if (packet->udp.dest != htons(CLIENT_PORT))
553                 return FALSE;
554
555         if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip)))
556                 return FALSE;
557
558         return TRUE;
559 }
560
561 static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
562 {
563         int bytes;
564         struct ip_udp_dhcp_packet packet;
565         uint16_t check;
566
567         memset(&packet, 0, sizeof(packet));
568
569         bytes = read(fd, &packet, sizeof(packet));
570         if (bytes < 0)
571                 return -1;
572
573         if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
574                 return -1;
575
576         if (bytes < ntohs(packet.ip.tot_len))
577                 /* packet is bigger than sizeof(packet), we did partial read */
578                 return -1;
579
580         /* ignore any extra garbage bytes */
581         bytes = ntohs(packet.ip.tot_len);
582
583         if (sanity_check(&packet, bytes) == FALSE)
584                 return -1;
585
586         check = packet.ip.check;
587         packet.ip.check = 0;
588         if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip)))
589                 return -1;
590
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))
598                 return -1;
599
600         memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) +
601                                                         sizeof(packet.udp)));
602
603         if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
604                 return -1;
605
606         return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
607 }
608
609 static void ipv4ll_start(GDHCPClient *dhcp_client)
610 {
611         guint timeout;
612         int seed;
613
614         if (dhcp_client->timeout > 0) {
615                 g_source_remove(dhcp_client->timeout);
616                 dhcp_client->timeout = 0;
617         }
618
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;
623
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);
627
628         /*first wait a random delay to avoid storm of arp request on boot*/
629         timeout = ipv4ll_random_delay_ms(PROBE_WAIT);
630
631         dhcp_client->retry_times++;
632         dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
633                                                 timeout,
634                                                 send_probe_packet,
635                                                 dhcp_client,
636                                                 NULL);
637 }
638
639 static void ipv4ll_stop(GDHCPClient *dhcp_client)
640 {
641
642         switch_listening_mode(dhcp_client, L_NONE);
643
644         if (dhcp_client->timeout > 0)
645                 g_source_remove(dhcp_client->timeout);
646
647         if (dhcp_client->listener_watch > 0) {
648                 g_source_remove(dhcp_client->listener_watch);
649                 dhcp_client->listener_watch = 0;
650         }
651
652         dhcp_client->state = IPV4LL_PROBE;
653         dhcp_client->retry_times = 0;
654         dhcp_client->requested_ip = 0;
655
656         g_free(dhcp_client->assigned_ip);
657         dhcp_client->assigned_ip = NULL;
658 }
659
660 static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
661 {
662         int bytes;
663         struct ether_arp arp;
664         uint32_t ip_requested;
665         int source_conflict;
666         int target_conflict;
667
668         memset(&arp, 0, sizeof(arp));
669         bytes = 0;
670         bytes = read(dhcp_client->listener_sockfd, &arp, sizeof(arp));
671         if (bytes < 0)
672                 return bytes;
673
674         if (arp.arp_op != htons(ARPOP_REPLY) &&
675                         arp.arp_op != htons(ARPOP_REQUEST))
676                 return -EINVAL;
677
678         ip_requested = ntohl(dhcp_client->requested_ip);
679         source_conflict = !memcmp(arp.arp_spa, &ip_requested,
680                                                 sizeof(ip_requested));
681
682         target_conflict = !memcmp(arp.arp_tpa, &ip_requested,
683                                 sizeof(ip_requested));
684
685         if (!source_conflict && !target_conflict)
686                 return 0;
687
688         dhcp_client->conflicts++;
689
690         debug(dhcp_client, "IPV4LL conflict detected");
691
692         if (dhcp_client->state == IPV4LL_MONITOR) {
693                 if (!source_conflict)
694                         return 0;
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);
700                 return 0;
701         }
702
703         if (dhcp_client->state == IPV4LL_DEFEND) {
704                 if (!source_conflict)
705                         return 0;
706                 else if (dhcp_client->ipv4ll_lost_cb != NULL)
707                         dhcp_client->ipv4ll_lost_cb(dhcp_client,
708                                                 dhcp_client->ipv4ll_lost_data);
709         }
710
711         ipv4ll_stop(dhcp_client);
712
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),
719                                         send_probe_packet,
720                                         dhcp_client,
721                                         NULL);
722         }
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.
726          */
727         else if (dhcp_client->no_lease_cb != NULL)
728                         dhcp_client->no_lease_cb(dhcp_client,
729                                                 dhcp_client->no_lease_data);
730
731         return 0;
732 }
733
734 static gboolean check_package_owner(GDHCPClient *dhcp_client,
735                                         struct dhcp_packet *packet)
736 {
737         if (packet->xid != dhcp_client->xid)
738                 return FALSE;
739
740         if (packet->hlen != 6)
741                 return FALSE;
742
743         if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
744                 return FALSE;
745
746         return TRUE;
747 }
748
749 static void start_request(GDHCPClient *dhcp_client);
750
751 static gboolean request_timeout(gpointer user_data)
752 {
753         GDHCPClient *dhcp_client = user_data;
754
755         debug(dhcp_client, "request timeout (retries %d)",
756                                         dhcp_client->retry_times);
757
758         dhcp_client->retry_times++;
759
760         start_request(dhcp_client);
761
762         return FALSE;
763 }
764
765 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
766                                                         gpointer user_data);
767
768 static int switch_listening_mode(GDHCPClient *dhcp_client,
769                                         ListenMode listen_mode)
770 {
771         GIOChannel *listener_channel;
772         int listener_sockfd;
773
774         debug(dhcp_client, "switch listening mode (%d ==> %d)",
775                                 dhcp_client->listen_mode, listen_mode);
776
777         if (dhcp_client->listen_mode == listen_mode)
778                 return 0;
779
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;
786         }
787
788         if (listen_mode == L_NONE)
789                 return 0;
790
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);
798         else
799                 return -EIO;
800
801         if (listener_sockfd < 0)
802                 return -EIO;
803
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);
808                 return -EIO;
809         }
810
811         dhcp_client->listen_mode = listen_mode;
812         dhcp_client->listener_sockfd = listener_sockfd;
813         dhcp_client->listener_channel = listener_channel;
814
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,
820                                                                 NULL);
821         g_io_channel_unref(dhcp_client->listener_channel);
822
823         return 0;
824 }
825
826 static void start_request(GDHCPClient *dhcp_client)
827 {
828         debug(dhcp_client, "start request (retries %d)",
829                                         dhcp_client->retry_times);
830
831         if (dhcp_client->retry_times == REQUEST_RETRIES) {
832                 dhcp_client->state = INIT_SELECTING;
833                 ipv4ll_start(dhcp_client);
834
835                 return;
836         }
837
838         if (dhcp_client->retry_times == 0) {
839                 dhcp_client->state = REQUESTING;
840                 switch_listening_mode(dhcp_client, L2);
841         }
842
843         send_select(dhcp_client);
844
845         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
846                                                         REQUEST_TIMEOUT,
847                                                         request_timeout,
848                                                         dhcp_client,
849                                                         NULL);
850 }
851
852 static uint32_t get_lease(struct dhcp_packet *packet)
853 {
854         uint8_t *option_u8;
855         uint32_t lease_seconds;
856
857         option_u8 = dhcp_get_option(packet, DHCP_LEASE_TIME);
858         if (option_u8 == NULL)
859                 return 3600;
860
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)
866                 lease_seconds = 10;
867
868         return lease_seconds;
869 }
870
871 static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times)
872 {
873         debug(dhcp_client, "restart DHCP (retries %d)", retry_times);
874
875         if (dhcp_client->timeout > 0) {
876                 g_source_remove(dhcp_client->timeout);
877                 dhcp_client->timeout = 0;
878         }
879
880         dhcp_client->retry_times = retry_times;
881         dhcp_client->requested_ip = 0;
882         switch_listening_mode(dhcp_client, L2);
883
884         g_dhcp_client_start(dhcp_client);
885 }
886
887 static gboolean start_rebound_timeout(gpointer user_data)
888 {
889         GDHCPClient *dhcp_client = user_data;
890
891         debug(dhcp_client, "start rebound timeout");
892
893         switch_listening_mode(dhcp_client, L2);
894
895         dhcp_client->lease_seconds >>= 1;
896
897         /* We need to have enough time to receive ACK package*/
898         if (dhcp_client->lease_seconds <= 6) {
899
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);
904
905                 restart_dhcp(dhcp_client, 0);
906         } else {
907                 send_rebound(dhcp_client);
908
909                 dhcp_client->timeout =
910                                 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
911                                                 dhcp_client->lease_seconds >> 1,
912                                                         start_rebound_timeout,
913                                                                 dhcp_client,
914                                                                 NULL);
915         }
916
917         return FALSE;
918 }
919
920 static void start_rebound(GDHCPClient *dhcp_client)
921 {
922         debug(dhcp_client, "start rebound");
923
924         dhcp_client->state = REBINDING;
925
926         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
927                                                 dhcp_client->lease_seconds >> 1,
928                                                         start_rebound_timeout,
929                                                                 dhcp_client,
930                                                                 NULL);
931 }
932
933 static gboolean start_renew_timeout(gpointer user_data)
934 {
935         GDHCPClient *dhcp_client = user_data;
936
937         debug(dhcp_client, "start renew timeout");
938
939         dhcp_client->state = RENEWING;
940
941         dhcp_client->lease_seconds >>= 1;
942
943         switch_listening_mode(dhcp_client, L3);
944         if (dhcp_client->lease_seconds <= 60)
945                 start_rebound(dhcp_client);
946         else {
947                 send_renew(dhcp_client);
948
949                 dhcp_client->timeout =
950                                 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
951                                                 dhcp_client->lease_seconds >> 1,
952                                                         start_renew_timeout,
953                                                                 dhcp_client,
954                                                                 NULL);
955         }
956
957         return FALSE;
958 }
959
960 static void start_bound(GDHCPClient *dhcp_client)
961 {
962         debug(dhcp_client, "start bound");
963
964         dhcp_client->state = BOUND;
965
966         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
967                                         dhcp_client->lease_seconds >> 1,
968                                         start_renew_timeout, dhcp_client,
969                                                         NULL);
970 }
971
972 static gboolean restart_dhcp_timeout(gpointer user_data)
973 {
974         GDHCPClient *dhcp_client = user_data;
975
976         debug(dhcp_client, "restart DHCP timeout");
977
978         dhcp_client->ack_retry_times++;
979
980         restart_dhcp(dhcp_client, dhcp_client->ack_retry_times);
981
982         return FALSE;
983 }
984
985 static char *get_ip(uint32_t ip)
986 {
987         struct in_addr addr;
988
989         addr.s_addr = ip;
990
991         return g_strdup(inet_ntoa(addr));
992 }
993
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 "),
997         [OPTION_STRING] = 1,
998         [OPTION_U8] = sizeof("255 "),
999         [OPTION_U16] = sizeof("65535 "),
1000         [OPTION_U32] = sizeof("4294967295 "),
1001 };
1002
1003 static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
1004 {
1005         return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
1006 }
1007
1008 /* Create "opt_value1 option_value2 ..." string */
1009 static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
1010 {
1011         unsigned upper_length;
1012         int len, optlen;
1013         char *dest, *ret;
1014
1015         len = option[OPT_LEN - OPT_DATA];
1016         type &= OPTION_TYPE_MASK;
1017         optlen = dhcp_option_lengths[type];
1018         if (optlen == 0)
1019                 return NULL;
1020         upper_length = len_of_option_as_string[type] *
1021                         ((unsigned)len / (unsigned)optlen);
1022         dest = ret = malloc(upper_length + 1);
1023         if (ret == NULL)
1024                 return NULL;
1025
1026         while (len >= optlen) {
1027                 switch (type) {
1028                 case OPTION_IP:
1029                         dest += sprint_nip(dest, "", option);
1030                         break;
1031                 case OPTION_U16: {
1032                         uint16_t val_u16 = dhcp_get_unaligned(
1033                                                 (uint16_t *) option);
1034                         dest += sprintf(dest, "%u", ntohs(val_u16));
1035                         break;
1036                 }
1037                 case OPTION_U32: {
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));
1042                         break;
1043                 }
1044                 case OPTION_STRING:
1045                         memcpy(dest, option, len);
1046                         dest[len] = '\0';
1047                         return ret;
1048                 default:
1049                         break;
1050                 }
1051                 option += optlen;
1052                 len -= optlen;
1053                 if (len <= 0)
1054                         break;
1055                 *dest++ = ' ';
1056                 *dest = '\0';
1057         }
1058
1059         return ret;
1060 }
1061
1062 static GList *get_option_value_list(char *value, GDHCPOptionType type)
1063 {
1064         char *pos = value;
1065         GList *list = NULL;
1066
1067         if (pos == NULL)
1068                 return NULL;
1069
1070         if (type == OPTION_STRING)
1071                 return g_list_append(list, g_strdup(value));
1072
1073         while ((pos = strchr(pos, ' ')) != NULL) {
1074                 *pos = '\0';
1075
1076                 list = g_list_append(list, g_strdup(value));
1077
1078                 value = ++pos;
1079         }
1080
1081         list = g_list_append(list, g_strdup(value));
1082
1083         return list;
1084 }
1085
1086 static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
1087 {
1088         GDHCPOptionType type;
1089         GList *list, *value_list;
1090         char *option_value;
1091         uint8_t *option;
1092         uint8_t code;
1093
1094         for (list = dhcp_client->request_list; list; list = list->next) {
1095                 code = (uint8_t) GPOINTER_TO_INT(list->data);
1096
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));
1101                         continue;
1102                 }
1103
1104                 type =  dhcp_get_code_type(code);
1105
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));
1110
1111                 value_list = get_option_value_list(option_value, type);
1112
1113                 g_free(option_value);
1114
1115                 if (value_list == NULL)
1116                         g_hash_table_remove(dhcp_client->code_value_hash,
1117                                                 GINT_TO_POINTER((int) code));
1118                 else
1119                         g_hash_table_insert(dhcp_client->code_value_hash,
1120                                 GINT_TO_POINTER((int) code), value_list);
1121         }
1122 }
1123
1124 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
1125                                                         gpointer user_data)
1126 {
1127         GDHCPClient *dhcp_client = user_data;
1128         struct dhcp_packet packet;
1129         uint8_t *message_type, *option_u8;
1130         int re;
1131
1132         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
1133                 dhcp_client->listener_watch = 0;
1134                 return FALSE;
1135         }
1136
1137         if (dhcp_client->listen_mode == L_NONE)
1138                 return FALSE;
1139
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);
1146                 return TRUE;
1147         }
1148         else
1149                 re = -EIO;
1150
1151         if (re < 0)
1152                 return TRUE;
1153
1154         if (check_package_owner(dhcp_client, &packet) == FALSE)
1155                 return TRUE;
1156
1157         message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
1158         if (message_type == NULL)
1159                 /* No message type option, ignore package */
1160                 return TRUE;
1161
1162         debug(dhcp_client, "received DHCP packet (current state %d)",
1163                                                         dhcp_client->state);
1164
1165         switch (dhcp_client->state) {
1166         case INIT_SELECTING:
1167                 if (*message_type != DHCPOFFER)
1168                         return TRUE;
1169
1170                 g_source_remove(dhcp_client->timeout);
1171                 dhcp_client->timeout = 0;
1172                 dhcp_client->retry_times = 0;
1173
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;
1178
1179                 dhcp_client->state = REQUESTING;
1180
1181                 start_request(dhcp_client);
1182
1183                 return TRUE;
1184         case REQUESTING:
1185         case RENEWING:
1186         case REBINDING:
1187                 if (*message_type == DHCPACK) {
1188                         dhcp_client->retry_times = 0;
1189
1190                         if (dhcp_client->timeout > 0)
1191                                 g_source_remove(dhcp_client->timeout);
1192                         dhcp_client->timeout = 0;
1193
1194                         dhcp_client->lease_seconds = get_lease(&packet);
1195
1196                         get_request(dhcp_client, &packet);
1197
1198                         switch_listening_mode(dhcp_client, L_NONE);
1199
1200                         g_free(dhcp_client->assigned_ip);
1201                         dhcp_client->assigned_ip = get_ip(packet.yiaddr);
1202
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);
1207
1208                         start_bound(dhcp_client);
1209                 } else if (*message_type == DHCPNAK) {
1210                         dhcp_client->retry_times = 0;
1211
1212                         if (dhcp_client->timeout > 0)
1213                                 g_source_remove(dhcp_client->timeout);
1214
1215                         dhcp_client->timeout = g_timeout_add_seconds_full(
1216                                                         G_PRIORITY_HIGH, 3,
1217                                                         restart_dhcp_timeout,
1218                                                         dhcp_client,
1219                                                         NULL);
1220                 }
1221
1222                 break;
1223         default:
1224                 break;
1225         }
1226
1227         debug(dhcp_client, "processed DHCP packet (new state %d)",
1228                                                         dhcp_client->state);
1229
1230         return TRUE;
1231 }
1232
1233 static gboolean discover_timeout(gpointer user_data)
1234 {
1235         GDHCPClient *dhcp_client = user_data;
1236
1237         dhcp_client->retry_times++;
1238
1239         g_dhcp_client_start(dhcp_client);
1240
1241         return FALSE;
1242 }
1243
1244 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data)
1245 {
1246         GDHCPClient *dhcp_client = dhcp_data;
1247
1248         debug(dhcp_client, "back to MONITOR mode");
1249
1250         dhcp_client->conflicts = 0;
1251         dhcp_client->state = IPV4LL_MONITOR;
1252
1253         return FALSE;
1254 }
1255
1256 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data)
1257 {
1258         GDHCPClient *dhcp_client = dhcp_data;
1259         uint32_t ip;
1260
1261         debug(dhcp_client, "request timeout (retries %d)",
1262                dhcp_client->retry_times);
1263
1264         if (dhcp_client->retry_times != ANNOUNCE_NUM){
1265                 dhcp_client->retry_times++;
1266                 send_announce_packet(dhcp_client);
1267                 return FALSE;
1268         }
1269
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);
1274
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;
1279
1280         return FALSE;
1281 }
1282
1283 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data)
1284 {
1285
1286         GDHCPClient *dhcp_client = dhcp_data;
1287
1288         debug(dhcp_client, "IPV4LL probe timeout (retries %d)",
1289                dhcp_client->retry_times);
1290
1291         if (dhcp_client->retry_times == PROBE_NUM) {
1292                 dhcp_client->state = IPV4LL_ANNOUNCE;
1293                 dhcp_client->retry_times = 0;
1294
1295                 dhcp_client->retry_times++;
1296                 send_announce_packet(dhcp_client);
1297                 return FALSE;
1298         }
1299         dhcp_client->retry_times++;
1300         send_probe_packet(dhcp_client);
1301
1302         return FALSE;
1303 }
1304
1305 int g_dhcp_client_start(GDHCPClient *dhcp_client)
1306 {
1307         int re;
1308
1309         if (dhcp_client->retry_times == DISCOVER_RETRIES) {
1310                 ipv4ll_start(dhcp_client);
1311                 return 0;
1312         }
1313
1314         if (dhcp_client->retry_times == 0) {
1315                 g_free(dhcp_client->assigned_ip);
1316                 dhcp_client->assigned_ip = NULL;
1317
1318                 dhcp_client->state = INIT_SELECTING;
1319                 re = switch_listening_mode(dhcp_client, L2);
1320                 if (re != 0)
1321                         return re;
1322
1323                 dhcp_client->xid = rand();
1324         }
1325
1326         send_discover(dhcp_client, 0);
1327
1328         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1329                                                         DISCOVER_TIMEOUT,
1330                                                         discover_timeout,
1331                                                         dhcp_client,
1332                                                         NULL);
1333         return 0;
1334 }
1335
1336 void g_dhcp_client_stop(GDHCPClient *dhcp_client)
1337 {
1338         switch_listening_mode(dhcp_client, L_NONE);
1339
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);
1345
1346         if (dhcp_client->timeout > 0) {
1347                 g_source_remove(dhcp_client->timeout);
1348                 dhcp_client->timeout = 0;
1349         }
1350
1351         if (dhcp_client->listener_watch > 0) {
1352                 g_source_remove(dhcp_client->listener_watch);
1353                 dhcp_client->listener_watch = 0;
1354         }
1355
1356         dhcp_client->listener_channel = NULL;
1357
1358         dhcp_client->retry_times = 0;
1359         dhcp_client->ack_retry_times = 0;
1360
1361         dhcp_client->requested_ip = 0;
1362         dhcp_client->state = RELEASED;
1363         dhcp_client->lease_seconds = 0;
1364 }
1365
1366 GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
1367                                         unsigned char option_code)
1368 {
1369         return g_hash_table_lookup(dhcp_client->code_value_hash,
1370                                         GINT_TO_POINTER((int) option_code));
1371 }
1372
1373 void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
1374                                         GDHCPClientEvent event,
1375                                         GDHCPClientEventFunc func,
1376                                                         gpointer data)
1377 {
1378         switch (event) {
1379         case G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE:
1380                 dhcp_client->lease_available_cb = func;
1381                 dhcp_client->lease_available_data = data;
1382                 return;
1383         case G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE:
1384                 dhcp_client->ipv4ll_available_cb = func;
1385                 dhcp_client->ipv4ll_available_data = data;
1386                 return;
1387         case G_DHCP_CLIENT_EVENT_NO_LEASE:
1388                 dhcp_client->no_lease_cb = func;
1389                 dhcp_client->no_lease_data = data;
1390                 return;
1391         case G_DHCP_CLIENT_EVENT_LEASE_LOST:
1392                 dhcp_client->lease_lost_cb = func;
1393                 dhcp_client->lease_lost_data = data;
1394                 return;
1395         case G_DHCP_CLIENT_EVENT_IPV4LL_LOST:
1396                 dhcp_client->ipv4ll_lost_cb = func;
1397                 dhcp_client->ipv4ll_lost_data = data;
1398                 return;
1399         case G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT:
1400                 dhcp_client->address_conflict_cb = func;
1401                 dhcp_client->address_conflict_data = data;
1402                 return;
1403         }
1404 }
1405
1406 int g_dhcp_client_get_index(GDHCPClient *dhcp_client)
1407 {
1408         return dhcp_client->ifindex;
1409 }
1410
1411 char *g_dhcp_client_get_address(GDHCPClient *dhcp_client)
1412 {
1413         return g_strdup(dhcp_client->assigned_ip);
1414 }
1415
1416 GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
1417                                                 unsigned char option_code)
1418 {
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)));
1424
1425         return G_DHCP_CLIENT_ERROR_NONE;
1426 }
1427
1428 static uint8_t *alloc_dhcp_option(int code, const char *str, int extra)
1429 {
1430         uint8_t *storage;
1431         int len = strnlen(str, 255);
1432
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);
1437
1438         return storage;
1439 }
1440
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)
1444 {
1445         uint8_t *binary_option;
1446
1447         if (option_code == G_DHCP_HOST_NAME && option_value != NULL) {
1448                 binary_option = alloc_dhcp_option(option_code,
1449                                                         option_value, 0);
1450
1451                 g_hash_table_insert(dhcp_client->send_value_hash,
1452                         GINT_TO_POINTER((int) option_code), binary_option);
1453         }
1454
1455         return G_DHCP_CLIENT_ERROR_NONE;
1456 }
1457
1458 GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client)
1459 {
1460         if (dhcp_client == NULL)
1461                 return NULL;
1462
1463         g_atomic_int_inc(&dhcp_client->ref_count);
1464
1465         return dhcp_client;
1466 }
1467
1468 void g_dhcp_client_unref(GDHCPClient *dhcp_client)
1469 {
1470         if (dhcp_client == NULL)
1471                 return;
1472
1473         if (g_atomic_int_dec_and_test(&dhcp_client->ref_count) == FALSE)
1474                 return;
1475
1476         g_dhcp_client_stop(dhcp_client);
1477
1478         g_free(dhcp_client->interface);
1479         g_free(dhcp_client->assigned_ip);
1480
1481         g_list_free(dhcp_client->request_list);
1482         g_list_free(dhcp_client->require_list);
1483
1484         g_hash_table_destroy(dhcp_client->code_value_hash);
1485         g_hash_table_destroy(dhcp_client->send_value_hash);
1486
1487         g_free(dhcp_client);
1488 }
1489
1490 void g_dhcp_client_set_debug(GDHCPClient *dhcp_client,
1491                                 GDHCPDebugFunc func, gpointer user_data)
1492 {
1493         if (dhcp_client == NULL)
1494                 return;
1495
1496         dhcp_client->debug_func = func;
1497         dhcp_client->debug_data = user_data;
1498 }