gdhcp: Check listerner_watch before remove it
[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 #define _GNU_SOURCE
27 #include <stdio.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/ioctl.h>
33 #include <arpa/inet.h>
34
35 #include <netpacket/packet.h>
36 #include <netinet/if_ether.h>
37 #include <net/ethernet.h>
38
39 #include <linux/if.h>
40 #include <linux/filter.h>
41
42 #include <glib.h>
43
44 #include "gdhcp.h"
45 #include "common.h"
46 #include "ipv4ll.h"
47
48 #define DISCOVER_TIMEOUT 3
49 #define DISCOVER_RETRIES 10
50
51 #define REQUEST_TIMEOUT 3
52 #define REQUEST_RETRIES 5
53
54 typedef enum _listen_mode {
55         L_NONE,
56         L2,
57         L3,
58         L_ARP,
59 } ListenMode;
60
61 typedef enum _dhcp_client_state {
62         INIT_SELECTING,
63         REQUESTING,
64         BOUND,
65         RENEWING,
66         REBINDING,
67         RELEASED,
68         IPV4LL_PROBE,
69         IPV4LL_ANNOUNCE,
70         IPV4LL_MONITOR,
71         IPV4LL_DEFEND,
72 } ClientState;
73
74 struct _GDHCPClient {
75         int ref_count;
76         GDHCPType type;
77         ClientState state;
78         int ifindex;
79         char *interface;
80         uint8_t mac_address[6];
81         uint32_t xid;
82         uint32_t server_ip;
83         uint32_t requested_ip;
84         char *assigned_ip;
85         uint32_t lease_seconds;
86         ListenMode listen_mode;
87         int listener_sockfd;
88         uint8_t retry_times;
89         uint8_t ack_retry_times;
90         uint8_t conflicts;
91         guint timeout;
92         guint listener_watch;
93         GIOChannel *listener_channel;
94         GList *require_list;
95         GList *request_list;
96         GHashTable *code_value_hash;
97         GHashTable *send_value_hash;
98         GDHCPClientEventFunc lease_available_cb;
99         gpointer lease_available_data;
100         GDHCPClientEventFunc ipv4ll_available_cb;
101         gpointer ipv4ll_available_data;
102         GDHCPClientEventFunc no_lease_cb;
103         gpointer no_lease_data;
104         GDHCPClientEventFunc lease_lost_cb;
105         gpointer lease_lost_data;
106         GDHCPClientEventFunc ipv4ll_lost_cb;
107         gpointer ipv4ll_lost_data;
108         GDHCPClientEventFunc address_conflict_cb;
109         gpointer address_conflict_data;
110         GDHCPDebugFunc debug_func;
111         gpointer debug_data;
112         char *last_address;
113 };
114
115 static inline void debug(GDHCPClient *client, const char *format, ...)
116 {
117         char str[256];
118         va_list ap;
119
120         if (client->debug_func == NULL)
121                 return;
122
123         va_start(ap, format);
124
125         if (vsnprintf(str, sizeof(str), format, ap) > 0)
126                 client->debug_func(str, client->debug_data);
127
128         va_end(ap);
129 }
130
131 /* Initialize the packet with the proper defaults */
132 static void init_packet(GDHCPClient *dhcp_client,
133                 struct dhcp_packet *packet, char type)
134 {
135         dhcp_init_header(packet, type);
136
137         memcpy(packet->chaddr, dhcp_client->mac_address, 6);
138 }
139
140 static void add_request_options(GDHCPClient *dhcp_client,
141                                 struct dhcp_packet *packet)
142 {
143         int len = 0;
144         GList *list;
145         uint8_t code;
146         int end = dhcp_end_option(packet->options);
147
148         for (list = dhcp_client->request_list; list; list = list->next) {
149                 code = (uint8_t) GPOINTER_TO_INT(list->data);
150
151                 packet->options[end + OPT_DATA + len] = code;
152                 len++;
153         }
154
155         if (len) {
156                 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
157                 packet->options[end + OPT_LEN] = len;
158                 packet->options[end + OPT_DATA + len] = DHCP_END;
159         }
160 }
161
162 static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
163 {
164         uint8_t *option = value;
165         struct dhcp_packet *packet = user_data;
166
167         dhcp_add_binary_option(packet, option);
168 }
169
170 static void add_send_options(GDHCPClient *dhcp_client,
171                                 struct dhcp_packet *packet)
172 {
173         g_hash_table_foreach(dhcp_client->send_value_hash,
174                                 add_binary_option, packet);
175 }
176
177 static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
178 {
179         struct dhcp_packet packet;
180
181         debug(dhcp_client, "sending DHCP discover request");
182
183         init_packet(dhcp_client, &packet, DHCPDISCOVER);
184
185         packet.xid = dhcp_client->xid;
186
187         if (requested)
188                 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
189
190         /* Explicitly saying that we want RFC-compliant packets helps
191          * some buggy DHCP servers to NOT send bigger packets */
192         dhcp_add_simple_option(&packet, DHCP_MAX_SIZE, htons(576));
193
194         add_request_options(dhcp_client, &packet);
195
196         add_send_options(dhcp_client, &packet);
197
198         return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
199                                         INADDR_BROADCAST, SERVER_PORT,
200                                         MAC_BCAST_ADDR, dhcp_client->ifindex);
201 }
202
203 static int send_select(GDHCPClient *dhcp_client)
204 {
205         struct dhcp_packet packet;
206
207         debug(dhcp_client, "sending DHCP select request");
208
209         init_packet(dhcp_client, &packet, DHCPREQUEST);
210
211         packet.xid = dhcp_client->xid;
212
213         dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP,
214                                         dhcp_client->requested_ip);
215         dhcp_add_simple_option(&packet, DHCP_SERVER_ID, dhcp_client->server_ip);
216
217         add_request_options(dhcp_client, &packet);
218
219         add_send_options(dhcp_client, &packet);
220
221         return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
222                                         INADDR_BROADCAST, SERVER_PORT,
223                                         MAC_BCAST_ADDR, dhcp_client->ifindex);
224 }
225
226 static int send_renew(GDHCPClient *dhcp_client)
227 {
228         struct dhcp_packet packet;
229
230         debug(dhcp_client, "sending DHCP renew request");
231
232         init_packet(dhcp_client , &packet, DHCPREQUEST);
233         packet.xid = dhcp_client->xid;
234         packet.ciaddr = dhcp_client->requested_ip;
235
236         add_request_options(dhcp_client, &packet);
237
238         add_send_options(dhcp_client, &packet);
239
240         return dhcp_send_kernel_packet(&packet,
241                 dhcp_client->requested_ip, CLIENT_PORT,
242                 dhcp_client->server_ip, SERVER_PORT);
243 }
244
245 static int send_rebound(GDHCPClient *dhcp_client)
246 {
247         struct dhcp_packet packet;
248
249         debug(dhcp_client, "sending DHCP rebound request");
250
251         init_packet(dhcp_client , &packet, DHCPREQUEST);
252         packet.xid = dhcp_client->xid;
253         packet.ciaddr = dhcp_client->requested_ip;
254
255         add_request_options(dhcp_client, &packet);
256
257         add_send_options(dhcp_client, &packet);
258
259         return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
260                                         INADDR_BROADCAST, SERVER_PORT,
261                                         MAC_BCAST_ADDR, dhcp_client->ifindex);
262 }
263
264 static int send_release(GDHCPClient *dhcp_client,
265                         uint32_t server, uint32_t ciaddr)
266 {
267         struct dhcp_packet packet;
268
269         debug(dhcp_client, "sending DHCP release request");
270
271         init_packet(dhcp_client, &packet, DHCPRELEASE);
272         packet.xid = rand();
273         packet.ciaddr = ciaddr;
274
275         dhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
276
277         return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
278                                                 server, SERVER_PORT);
279 }
280
281 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data);
282 static int switch_listening_mode(GDHCPClient *dhcp_client,
283                                         ListenMode listen_mode);
284
285 static gboolean send_probe_packet(gpointer dhcp_data)
286 {
287         GDHCPClient *dhcp_client;
288         guint timeout;
289
290         dhcp_client = dhcp_data;
291         /* if requested_ip is not valid, pick a new address*/
292         if (dhcp_client->requested_ip == 0) {
293                 debug(dhcp_client, "pick a new random address");
294                 dhcp_client->requested_ip = ipv4ll_random_ip(0);
295         }
296
297         debug(dhcp_client, "sending IPV4LL probe request");
298
299         if (dhcp_client->retry_times == 1) {
300                 dhcp_client->state = IPV4LL_PROBE;
301                 switch_listening_mode(dhcp_client, L_ARP);
302         }
303         ipv4ll_send_arp_packet(dhcp_client->mac_address, 0,
304                         dhcp_client->requested_ip, dhcp_client->ifindex);
305
306         if (dhcp_client->retry_times < PROBE_NUM) {
307                 /*add a random timeout in range of PROBE_MIN to PROBE_MAX*/
308                 timeout = ipv4ll_random_delay_ms(PROBE_MAX-PROBE_MIN);
309                 timeout += PROBE_MIN*1000;
310         } else
311                 timeout = (ANNOUNCE_WAIT * 1000);
312
313         dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
314                                                  timeout,
315                                                  ipv4ll_probe_timeout,
316                                                  dhcp_client,
317                                                  NULL);
318         return FALSE;
319 }
320
321 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data);
322 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data);
323
324 static gboolean send_announce_packet(gpointer dhcp_data)
325 {
326         GDHCPClient *dhcp_client;
327
328         dhcp_client = dhcp_data;
329
330         debug(dhcp_client, "sending IPV4LL announce request");
331
332         ipv4ll_send_arp_packet(dhcp_client->mac_address,
333                                 dhcp_client->requested_ip,
334                                 dhcp_client->requested_ip,
335                                 dhcp_client->ifindex);
336
337         if (dhcp_client->timeout > 0)
338                 g_source_remove(dhcp_client->timeout);
339         dhcp_client->timeout = 0;
340
341         if (dhcp_client->state == IPV4LL_DEFEND) {
342                 dhcp_client->timeout =
343                         g_timeout_add_seconds_full(G_PRIORITY_HIGH,
344                                                 DEFEND_INTERVAL,
345                                                 ipv4ll_defend_timeout,
346                                                 dhcp_client,
347                                                 NULL);
348                 return TRUE;
349         } else
350                 dhcp_client->timeout =
351                         g_timeout_add_seconds_full(G_PRIORITY_HIGH,
352                                                 ANNOUNCE_INTERVAL,
353                                                 ipv4ll_announce_timeout,
354                                                 dhcp_client,
355                                                 NULL);
356         return TRUE;
357 }
358
359 static void get_interface_mac_address(int index, uint8_t *mac_address)
360 {
361         struct ifreq ifr;
362         int sk, err;
363
364         sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
365         if (sk < 0) {
366                 perror("Open socket error");
367                 return;
368         }
369
370         memset(&ifr, 0, sizeof(ifr));
371         ifr.ifr_ifindex = index;
372
373         err = ioctl(sk, SIOCGIFNAME, &ifr);
374         if (err < 0) {
375                 perror("Get interface name error");
376                 goto done;
377         }
378
379         err = ioctl(sk, SIOCGIFHWADDR, &ifr);
380         if (err < 0) {
381                 perror("Get mac address error");
382                 goto done;
383         }
384
385         memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
386
387 done:
388         close(sk);
389 }
390
391 static void remove_value(gpointer data, gpointer user_data)
392 {
393         char *value = data;
394         g_free(value);
395 }
396
397 static void remove_option_value(gpointer data)
398 {
399         GList *option_value = data;
400
401         g_list_foreach(option_value, remove_value, NULL);
402 }
403
404 GDHCPClient *g_dhcp_client_new(GDHCPType type,
405                         int ifindex, GDHCPClientError *error)
406 {
407         GDHCPClient *dhcp_client;
408
409         if (ifindex < 0) {
410                 *error = G_DHCP_CLIENT_ERROR_INVALID_INDEX;
411                 return NULL;
412         }
413
414         dhcp_client = g_try_new0(GDHCPClient, 1);
415         if (dhcp_client == NULL) {
416                 *error = G_DHCP_CLIENT_ERROR_NOMEM;
417                 return NULL;
418         }
419
420         dhcp_client->interface = get_interface_name(ifindex);
421         if (dhcp_client->interface == NULL) {
422                 *error = G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE;
423                 goto error;
424         }
425
426         if (interface_is_up(ifindex) == FALSE) {
427                 *error = G_DHCP_CLIENT_ERROR_INTERFACE_DOWN;
428                 goto error;
429         }
430
431         get_interface_mac_address(ifindex, dhcp_client->mac_address);
432
433         dhcp_client->listener_sockfd = -1;
434         dhcp_client->listener_channel = NULL;
435         dhcp_client->listen_mode = L_NONE;
436         dhcp_client->ref_count = 1;
437         dhcp_client->type = type;
438         dhcp_client->ifindex = ifindex;
439         dhcp_client->lease_available_cb = NULL;
440         dhcp_client->ipv4ll_available_cb = NULL;
441         dhcp_client->no_lease_cb = NULL;
442         dhcp_client->lease_lost_cb = NULL;
443         dhcp_client->ipv4ll_lost_cb = NULL;
444         dhcp_client->address_conflict_cb = NULL;
445         dhcp_client->listener_watch = 0;
446         dhcp_client->retry_times = 0;
447         dhcp_client->ack_retry_times = 0;
448         dhcp_client->code_value_hash = g_hash_table_new_full(g_direct_hash,
449                                 g_direct_equal, NULL, remove_option_value);
450         dhcp_client->send_value_hash = g_hash_table_new_full(g_direct_hash,
451                                 g_direct_equal, NULL, g_free);
452         dhcp_client->request_list = NULL;
453         dhcp_client->require_list = NULL;
454
455         *error = G_DHCP_CLIENT_ERROR_NONE;
456
457         return dhcp_client;
458
459 error:
460         g_free(dhcp_client->interface);
461         g_free(dhcp_client);
462         return NULL;
463 }
464
465 #define SERVER_AND_CLIENT_PORTS  ((67 << 16) + 68)
466
467 static int dhcp_l2_socket(int ifindex)
468 {
469         int fd;
470         struct sockaddr_ll sock;
471
472         /*
473          * Comment:
474          *
475          *      I've selected not to see LL header, so BPF doesn't see it, too.
476          *      The filter may also pass non-IP and non-ARP packets, but we do
477          *      a more complete check when receiving the message in userspace.
478          *
479          * and filter shamelessly stolen from:
480          *
481          *      http://www.flamewarmaster.de/software/dhcpclient/
482          *
483          * There are a few other interesting ideas on that page (look under
484          * "Motivation").  Use of netlink events is most interesting.  Think
485          * of various network servers listening for events and reconfiguring.
486          * That would obsolete sending HUP signals and/or make use of restarts.
487          *
488          * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
489          * License: GPL v2.
490          *
491          * TODO: make conditional?
492          */
493         static const struct sock_filter filter_instr[] = {
494                 /* check for udp */
495                 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
496                 /* L5, L1, is UDP? */
497                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0),
498                 /* ugly check for arp on ethernet-like and IPv4 */
499                 BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */
500                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4),/* L3, L4 */
501                 /* skip IP header */
502                 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */
503                 /* check udp source and destination ports */
504                 BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
505                 /* L3, L4 */
506                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),
507                 /* returns */
508                 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* L3: pass */
509                 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
510         };
511
512         static const struct sock_fprog filter_prog = {
513                 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
514                 /* casting const away: */
515                 .filter = (struct sock_filter *) filter_instr,
516         };
517
518         fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
519         if (fd < 0)
520                 return fd;
521
522         if (SERVER_PORT == 67 && CLIENT_PORT == 68)
523                 /* Use only if standard ports are in use */
524                 setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
525                                                         sizeof(filter_prog));
526
527         memset(&sock, 0, sizeof(sock));
528         sock.sll_family = AF_PACKET;
529         sock.sll_protocol = htons(ETH_P_IP);
530         sock.sll_ifindex = ifindex;
531
532         if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
533                 close(fd);
534                 return -errno;
535         }
536
537         return fd;
538 }
539
540 static gboolean sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
541 {
542         if (packet->ip.protocol != IPPROTO_UDP)
543                 return FALSE;
544
545         if (packet->ip.version != IPVERSION)
546                 return FALSE;
547
548         if (packet->ip.ihl != sizeof(packet->ip) >> 2)
549                 return FALSE;
550
551         if (packet->udp.dest != htons(CLIENT_PORT))
552                 return FALSE;
553
554         if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip)))
555                 return FALSE;
556
557         return TRUE;
558 }
559
560 static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
561 {
562         int bytes;
563         struct ip_udp_dhcp_packet packet;
564         uint16_t check;
565
566         memset(&packet, 0, sizeof(packet));
567
568         bytes = read(fd, &packet, sizeof(packet));
569         if (bytes < 0)
570                 return -1;
571
572         if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
573                 return -1;
574
575         if (bytes < ntohs(packet.ip.tot_len))
576                 /* packet is bigger than sizeof(packet), we did partial read */
577                 return -1;
578
579         /* ignore any extra garbage bytes */
580         bytes = ntohs(packet.ip.tot_len);
581
582         if (sanity_check(&packet, bytes) == FALSE)
583                 return -1;
584
585         check = packet.ip.check;
586         packet.ip.check = 0;
587         if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip)))
588                 return -1;
589
590         /* verify UDP checksum. IP header has to be modified for this */
591         memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
592         /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
593         packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
594         check = packet.udp.check;
595         packet.udp.check = 0;
596         if (check && check != dhcp_checksum(&packet, bytes))
597                 return -1;
598
599         memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) +
600                                                         sizeof(packet.udp)));
601
602         if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
603                 return -1;
604
605         return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
606 }
607
608 static void ipv4ll_start(GDHCPClient *dhcp_client)
609 {
610         guint timeout;
611         int seed;
612
613         if (dhcp_client->timeout > 0) {
614                 g_source_remove(dhcp_client->timeout);
615                 dhcp_client->timeout = 0;
616         }
617
618         switch_listening_mode(dhcp_client, L_NONE);
619         dhcp_client->type = G_DHCP_IPV4LL;
620         dhcp_client->retry_times = 0;
621         dhcp_client->requested_ip = 0;
622
623         /*try to start with a based mac address ip*/
624         seed = (dhcp_client->mac_address[4] << 8 | dhcp_client->mac_address[4]);
625         dhcp_client->requested_ip = ipv4ll_random_ip(seed);
626
627         /*first wait a random delay to avoid storm of arp request on boot*/
628         timeout = ipv4ll_random_delay_ms(PROBE_WAIT);
629
630         dhcp_client->retry_times++;
631         dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
632                                                 timeout,
633                                                 send_probe_packet,
634                                                 dhcp_client,
635                                                 NULL);
636 }
637
638 static void ipv4ll_stop(GDHCPClient *dhcp_client)
639 {
640
641         switch_listening_mode(dhcp_client, L_NONE);
642
643         if (dhcp_client->timeout > 0)
644                 g_source_remove(dhcp_client->timeout);
645
646         if (dhcp_client->listener_watch > 0) {
647                 g_source_remove(dhcp_client->listener_watch);
648                 dhcp_client->listener_watch = 0;
649         }
650
651         dhcp_client->state = IPV4LL_PROBE;
652         dhcp_client->retry_times = 0;
653         dhcp_client->requested_ip = 0;
654
655         g_free(dhcp_client->assigned_ip);
656         dhcp_client->assigned_ip = NULL;
657 }
658
659 static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
660 {
661         int bytes;
662         struct ether_arp arp;
663         uint32_t ip_requested;
664         int source_conflict;
665         int target_conflict;
666
667         memset(&arp, 0, sizeof(arp));
668         bytes = 0;
669         bytes = read(dhcp_client->listener_sockfd, &arp, sizeof(arp));
670         if (bytes < 0)
671                 return bytes;
672
673         if (arp.arp_op != htons(ARPOP_REPLY) &&
674                         arp.arp_op != htons(ARPOP_REQUEST))
675                 return -EINVAL;
676
677         ip_requested = ntohl(dhcp_client->requested_ip);
678         source_conflict = !memcmp(arp.arp_spa, &ip_requested,
679                                                 sizeof(ip_requested));
680
681         target_conflict = !memcmp(arp.arp_tpa, &ip_requested,
682                                 sizeof(ip_requested));
683
684         if (!source_conflict && !target_conflict)
685                 return 0;
686
687         dhcp_client->conflicts++;
688
689         debug(dhcp_client, "IPV4LL conflict detected");
690
691         if (dhcp_client->state == IPV4LL_MONITOR) {
692                 if (!source_conflict)
693                         return 0;
694                 dhcp_client->state = IPV4LL_DEFEND;
695                 debug(dhcp_client, "DEFEND mode conflicts : %d",
696                         dhcp_client->conflicts);
697                 /*Try to defend with a single announce*/
698                 send_announce_packet(dhcp_client);
699                 return 0;
700         }
701
702         if (dhcp_client->state == IPV4LL_DEFEND) {
703                 if (!source_conflict)
704                         return 0;
705                 else if (dhcp_client->ipv4ll_lost_cb != NULL)
706                         dhcp_client->ipv4ll_lost_cb(dhcp_client,
707                                                 dhcp_client->ipv4ll_lost_data);
708         }
709
710         ipv4ll_stop(dhcp_client);
711
712         if (dhcp_client->conflicts < MAX_CONFLICTS) {
713                 /*restart whole state machine*/
714                 dhcp_client->retry_times++;
715                 dhcp_client->timeout =
716                         g_timeout_add_full(G_PRIORITY_HIGH,
717                                         ipv4ll_random_delay_ms(PROBE_WAIT),
718                                         send_probe_packet,
719                                         dhcp_client,
720                                         NULL);
721         }
722         /* Here we got a lot of conflicts, RFC3927 states that we have
723          * to wait RATE_LIMIT_INTERVAL before retrying,
724          * but we just report failure.
725          */
726         else if (dhcp_client->no_lease_cb != NULL)
727                         dhcp_client->no_lease_cb(dhcp_client,
728                                                 dhcp_client->no_lease_data);
729
730         return 0;
731 }
732
733 static gboolean check_package_owner(GDHCPClient *dhcp_client,
734                                         struct dhcp_packet *packet)
735 {
736         if (packet->xid != dhcp_client->xid)
737                 return FALSE;
738
739         if (packet->hlen != 6)
740                 return FALSE;
741
742         if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
743                 return FALSE;
744
745         return TRUE;
746 }
747
748 static void start_request(GDHCPClient *dhcp_client);
749
750 static gboolean request_timeout(gpointer user_data)
751 {
752         GDHCPClient *dhcp_client = user_data;
753
754         debug(dhcp_client, "request timeout (retries %d)",
755                                         dhcp_client->retry_times);
756
757         dhcp_client->retry_times++;
758
759         start_request(dhcp_client);
760
761         return FALSE;
762 }
763
764 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
765                                                         gpointer user_data);
766
767 static int switch_listening_mode(GDHCPClient *dhcp_client,
768                                         ListenMode listen_mode)
769 {
770         GIOChannel *listener_channel;
771         int listener_sockfd;
772
773         debug(dhcp_client, "switch listening mode (%d ==> %d)",
774                                 dhcp_client->listen_mode, listen_mode);
775
776         if (dhcp_client->listen_mode == listen_mode)
777                 return 0;
778
779         if (dhcp_client->listen_mode != L_NONE) {
780                 if (dhcp_client->listener_watch > 0)
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, dhcp_client->last_address);
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                 if (dhcp_client->timeout > 0)
950                         g_source_remove(dhcp_client->timeout);
951
952                 dhcp_client->timeout =
953                                 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
954                                                 dhcp_client->lease_seconds >> 1,
955                                                         start_renew_timeout,
956                                                                 dhcp_client,
957                                                                 NULL);
958         }
959
960         return FALSE;
961 }
962
963 static void start_bound(GDHCPClient *dhcp_client)
964 {
965         debug(dhcp_client, "start bound");
966
967         dhcp_client->state = BOUND;
968
969         if (dhcp_client->timeout > 0)
970                 g_source_remove(dhcp_client->timeout);
971
972         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
973                                         dhcp_client->lease_seconds >> 1,
974                                         start_renew_timeout, dhcp_client,
975                                                         NULL);
976 }
977
978 static gboolean restart_dhcp_timeout(gpointer user_data)
979 {
980         GDHCPClient *dhcp_client = user_data;
981
982         debug(dhcp_client, "restart DHCP timeout");
983
984         dhcp_client->ack_retry_times++;
985
986         restart_dhcp(dhcp_client, dhcp_client->ack_retry_times);
987
988         return FALSE;
989 }
990
991 static char *get_ip(uint32_t ip)
992 {
993         struct in_addr addr;
994
995         addr.s_addr = ip;
996
997         return g_strdup(inet_ntoa(addr));
998 }
999
1000 /* get a rough idea of how long an option will be */
1001 static const uint8_t len_of_option_as_string[] = {
1002         [OPTION_IP] = sizeof("255.255.255.255 "),
1003         [OPTION_STRING] = 1,
1004         [OPTION_U8] = sizeof("255 "),
1005         [OPTION_U16] = sizeof("65535 "),
1006         [OPTION_U32] = sizeof("4294967295 "),
1007 };
1008
1009 static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
1010 {
1011         return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
1012 }
1013
1014 /* Create "opt_value1 option_value2 ..." string */
1015 static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
1016 {
1017         unsigned upper_length;
1018         int len, optlen;
1019         char *dest, *ret;
1020
1021         len = option[OPT_LEN - OPT_DATA];
1022         type &= OPTION_TYPE_MASK;
1023         optlen = dhcp_option_lengths[type];
1024         if (optlen == 0)
1025                 return NULL;
1026         upper_length = len_of_option_as_string[type] *
1027                         ((unsigned)len / (unsigned)optlen);
1028         dest = ret = malloc(upper_length + 1);
1029         if (ret == NULL)
1030                 return NULL;
1031
1032         while (len >= optlen) {
1033                 switch (type) {
1034                 case OPTION_IP:
1035                         dest += sprint_nip(dest, "", option);
1036                         break;
1037                 case OPTION_U16: {
1038                         uint16_t val_u16 = dhcp_get_unaligned(
1039                                                 (uint16_t *) option);
1040                         dest += sprintf(dest, "%u", ntohs(val_u16));
1041                         break;
1042                 }
1043                 case OPTION_U32: {
1044                         uint32_t val_u32 = dhcp_get_unaligned(
1045                                                 (uint32_t *) option);
1046                         dest += sprintf(dest, type == OPTION_U32 ? "%lu" :
1047                                         "%ld", (unsigned long) ntohl(val_u32));
1048                         break;
1049                 }
1050                 case OPTION_STRING:
1051                         memcpy(dest, option, len);
1052                         dest[len] = '\0';
1053                         return ret;
1054                 default:
1055                         break;
1056                 }
1057                 option += optlen;
1058                 len -= optlen;
1059                 if (len <= 0)
1060                         break;
1061                 *dest++ = ' ';
1062                 *dest = '\0';
1063         }
1064
1065         return ret;
1066 }
1067
1068 static GList *get_option_value_list(char *value, GDHCPOptionType type)
1069 {
1070         char *pos = value;
1071         GList *list = NULL;
1072
1073         if (pos == NULL)
1074                 return NULL;
1075
1076         if (type == OPTION_STRING)
1077                 return g_list_append(list, g_strdup(value));
1078
1079         while ((pos = strchr(pos, ' ')) != NULL) {
1080                 *pos = '\0';
1081
1082                 list = g_list_append(list, g_strdup(value));
1083
1084                 value = ++pos;
1085         }
1086
1087         list = g_list_append(list, g_strdup(value));
1088
1089         return list;
1090 }
1091
1092 static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
1093 {
1094         GDHCPOptionType type;
1095         GList *list, *value_list;
1096         char *option_value;
1097         uint8_t *option;
1098         uint8_t code;
1099
1100         for (list = dhcp_client->request_list; list; list = list->next) {
1101                 code = (uint8_t) GPOINTER_TO_INT(list->data);
1102
1103                 option = dhcp_get_option(packet, code);
1104                 if (option == NULL) {
1105                         g_hash_table_remove(dhcp_client->code_value_hash,
1106                                                 GINT_TO_POINTER((int) code));
1107                         continue;
1108                 }
1109
1110                 type =  dhcp_get_code_type(code);
1111
1112                 option_value = malloc_option_value_string(option, type);
1113                 if (option_value == NULL)
1114                         g_hash_table_remove(dhcp_client->code_value_hash,
1115                                                 GINT_TO_POINTER((int) code));
1116
1117                 value_list = get_option_value_list(option_value, type);
1118
1119                 g_free(option_value);
1120
1121                 if (value_list == NULL)
1122                         g_hash_table_remove(dhcp_client->code_value_hash,
1123                                                 GINT_TO_POINTER((int) code));
1124                 else
1125                         g_hash_table_insert(dhcp_client->code_value_hash,
1126                                 GINT_TO_POINTER((int) code), value_list);
1127         }
1128 }
1129
1130 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
1131                                                         gpointer user_data)
1132 {
1133         GDHCPClient *dhcp_client = user_data;
1134         struct dhcp_packet packet;
1135         uint8_t *message_type, *option_u8;
1136         int re;
1137
1138         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
1139                 dhcp_client->listener_watch = 0;
1140                 return FALSE;
1141         }
1142
1143         if (dhcp_client->listen_mode == L_NONE)
1144                 return FALSE;
1145
1146         if (dhcp_client->listen_mode == L2)
1147                 re = dhcp_recv_l2_packet(&packet, dhcp_client->listener_sockfd);
1148         else if (dhcp_client->listen_mode == L3)
1149                 re = dhcp_recv_l3_packet(&packet, dhcp_client->listener_sockfd);
1150         else if (dhcp_client->listen_mode == L_ARP) {
1151                 re = ipv4ll_recv_arp_packet(dhcp_client);
1152                 return TRUE;
1153         }
1154         else
1155                 re = -EIO;
1156
1157         if (re < 0)
1158                 return TRUE;
1159
1160         if (check_package_owner(dhcp_client, &packet) == FALSE)
1161                 return TRUE;
1162
1163         message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
1164         if (message_type == NULL)
1165                 /* No message type option, ignore package */
1166                 return TRUE;
1167
1168         debug(dhcp_client, "received DHCP packet (current state %d)",
1169                                                         dhcp_client->state);
1170
1171         switch (dhcp_client->state) {
1172         case INIT_SELECTING:
1173                 if (*message_type != DHCPOFFER)
1174                         return TRUE;
1175
1176                 g_source_remove(dhcp_client->timeout);
1177                 dhcp_client->timeout = 0;
1178                 dhcp_client->retry_times = 0;
1179
1180                 option_u8 = dhcp_get_option(&packet, DHCP_SERVER_ID);
1181                 dhcp_client->server_ip =
1182                                 dhcp_get_unaligned((uint32_t *) option_u8);
1183                 dhcp_client->requested_ip = packet.yiaddr;
1184
1185                 dhcp_client->state = REQUESTING;
1186
1187                 start_request(dhcp_client);
1188
1189                 return TRUE;
1190         case REQUESTING:
1191         case RENEWING:
1192         case REBINDING:
1193                 if (*message_type == DHCPACK) {
1194                         dhcp_client->retry_times = 0;
1195
1196                         if (dhcp_client->timeout > 0)
1197                                 g_source_remove(dhcp_client->timeout);
1198                         dhcp_client->timeout = 0;
1199
1200                         dhcp_client->lease_seconds = get_lease(&packet);
1201
1202                         get_request(dhcp_client, &packet);
1203
1204                         switch_listening_mode(dhcp_client, L_NONE);
1205
1206                         g_free(dhcp_client->assigned_ip);
1207                         dhcp_client->assigned_ip = get_ip(packet.yiaddr);
1208
1209                         /* Address should be set up here */
1210                         if (dhcp_client->lease_available_cb != NULL)
1211                                 dhcp_client->lease_available_cb(dhcp_client,
1212                                         dhcp_client->lease_available_data);
1213
1214                         start_bound(dhcp_client);
1215                 } else if (*message_type == DHCPNAK) {
1216                         dhcp_client->retry_times = 0;
1217
1218                         if (dhcp_client->timeout > 0)
1219                                 g_source_remove(dhcp_client->timeout);
1220
1221                         dhcp_client->timeout = g_timeout_add_seconds_full(
1222                                                         G_PRIORITY_HIGH, 3,
1223                                                         restart_dhcp_timeout,
1224                                                         dhcp_client,
1225                                                         NULL);
1226                 }
1227
1228                 break;
1229         default:
1230                 break;
1231         }
1232
1233         debug(dhcp_client, "processed DHCP packet (new state %d)",
1234                                                         dhcp_client->state);
1235
1236         return TRUE;
1237 }
1238
1239 static gboolean discover_timeout(gpointer user_data)
1240 {
1241         GDHCPClient *dhcp_client = user_data;
1242
1243         dhcp_client->retry_times++;
1244
1245         /*
1246          * We do not send the REQUESTED IP option if we are retrying because
1247          * if the server is non-authoritative it will ignore the request if the
1248          * option is present.
1249          */
1250         g_dhcp_client_start(dhcp_client, NULL);
1251
1252         return FALSE;
1253 }
1254
1255 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data)
1256 {
1257         GDHCPClient *dhcp_client = dhcp_data;
1258
1259         debug(dhcp_client, "back to MONITOR mode");
1260
1261         dhcp_client->conflicts = 0;
1262         dhcp_client->state = IPV4LL_MONITOR;
1263
1264         return FALSE;
1265 }
1266
1267 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data)
1268 {
1269         GDHCPClient *dhcp_client = dhcp_data;
1270         uint32_t ip;
1271
1272         debug(dhcp_client, "request timeout (retries %d)",
1273                dhcp_client->retry_times);
1274
1275         if (dhcp_client->retry_times != ANNOUNCE_NUM){
1276                 dhcp_client->retry_times++;
1277                 send_announce_packet(dhcp_client);
1278                 return FALSE;
1279         }
1280
1281         ip = htonl(dhcp_client->requested_ip);
1282         debug(dhcp_client, "switching to monitor mode");
1283         dhcp_client->state = IPV4LL_MONITOR;
1284         dhcp_client->assigned_ip = get_ip(ip);
1285
1286         if (dhcp_client->ipv4ll_available_cb != NULL)
1287                 dhcp_client->ipv4ll_available_cb(dhcp_client,
1288                                         dhcp_client->ipv4ll_available_data);
1289         dhcp_client->conflicts = 0;
1290
1291         return FALSE;
1292 }
1293
1294 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data)
1295 {
1296
1297         GDHCPClient *dhcp_client = dhcp_data;
1298
1299         debug(dhcp_client, "IPV4LL probe timeout (retries %d)",
1300                dhcp_client->retry_times);
1301
1302         if (dhcp_client->retry_times == PROBE_NUM) {
1303                 dhcp_client->state = IPV4LL_ANNOUNCE;
1304                 dhcp_client->retry_times = 0;
1305
1306                 dhcp_client->retry_times++;
1307                 send_announce_packet(dhcp_client);
1308                 return FALSE;
1309         }
1310         dhcp_client->retry_times++;
1311         send_probe_packet(dhcp_client);
1312
1313         return FALSE;
1314 }
1315
1316 int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
1317 {
1318         int re;
1319         uint32_t addr;
1320
1321         if (dhcp_client->retry_times == DISCOVER_RETRIES) {
1322                 ipv4ll_start(dhcp_client);
1323                 return 0;
1324         }
1325
1326         if (dhcp_client->retry_times == 0) {
1327                 g_free(dhcp_client->assigned_ip);
1328                 dhcp_client->assigned_ip = NULL;
1329
1330                 dhcp_client->state = INIT_SELECTING;
1331                 re = switch_listening_mode(dhcp_client, L2);
1332                 if (re != 0)
1333                         return re;
1334
1335                 dhcp_client->xid = rand();
1336         }
1337
1338         if (last_address == NULL) {
1339                 addr = 0;
1340         } else {
1341                 addr = inet_addr(last_address);
1342                 if (addr == 0xFFFFFFFF) {
1343                         addr = 0;
1344                 } else {
1345                         g_free(dhcp_client->last_address);
1346                         dhcp_client->last_address = g_strdup(last_address);
1347                 }
1348         }
1349         send_discover(dhcp_client, addr);
1350
1351         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1352                                                         DISCOVER_TIMEOUT,
1353                                                         discover_timeout,
1354                                                         dhcp_client,
1355                                                         NULL);
1356         return 0;
1357 }
1358
1359 void g_dhcp_client_stop(GDHCPClient *dhcp_client)
1360 {
1361         switch_listening_mode(dhcp_client, L_NONE);
1362
1363         if (dhcp_client->state == BOUND ||
1364                         dhcp_client->state == RENEWING ||
1365                                 dhcp_client->state == REBINDING)
1366                 send_release(dhcp_client, dhcp_client->server_ip,
1367                                         dhcp_client->requested_ip);
1368
1369         if (dhcp_client->timeout > 0) {
1370                 g_source_remove(dhcp_client->timeout);
1371                 dhcp_client->timeout = 0;
1372         }
1373
1374         if (dhcp_client->listener_watch > 0) {
1375                 g_source_remove(dhcp_client->listener_watch);
1376                 dhcp_client->listener_watch = 0;
1377         }
1378
1379         dhcp_client->listener_channel = NULL;
1380
1381         dhcp_client->retry_times = 0;
1382         dhcp_client->ack_retry_times = 0;
1383
1384         dhcp_client->requested_ip = 0;
1385         dhcp_client->state = RELEASED;
1386         dhcp_client->lease_seconds = 0;
1387 }
1388
1389 GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
1390                                         unsigned char option_code)
1391 {
1392         return g_hash_table_lookup(dhcp_client->code_value_hash,
1393                                         GINT_TO_POINTER((int) option_code));
1394 }
1395
1396 void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
1397                                         GDHCPClientEvent event,
1398                                         GDHCPClientEventFunc func,
1399                                                         gpointer data)
1400 {
1401         switch (event) {
1402         case G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE:
1403                 dhcp_client->lease_available_cb = func;
1404                 dhcp_client->lease_available_data = data;
1405                 return;
1406         case G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE:
1407                 dhcp_client->ipv4ll_available_cb = func;
1408                 dhcp_client->ipv4ll_available_data = data;
1409                 return;
1410         case G_DHCP_CLIENT_EVENT_NO_LEASE:
1411                 dhcp_client->no_lease_cb = func;
1412                 dhcp_client->no_lease_data = data;
1413                 return;
1414         case G_DHCP_CLIENT_EVENT_LEASE_LOST:
1415                 dhcp_client->lease_lost_cb = func;
1416                 dhcp_client->lease_lost_data = data;
1417                 return;
1418         case G_DHCP_CLIENT_EVENT_IPV4LL_LOST:
1419                 dhcp_client->ipv4ll_lost_cb = func;
1420                 dhcp_client->ipv4ll_lost_data = data;
1421                 return;
1422         case G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT:
1423                 dhcp_client->address_conflict_cb = func;
1424                 dhcp_client->address_conflict_data = data;
1425                 return;
1426         }
1427 }
1428
1429 int g_dhcp_client_get_index(GDHCPClient *dhcp_client)
1430 {
1431         return dhcp_client->ifindex;
1432 }
1433
1434 char *g_dhcp_client_get_address(GDHCPClient *dhcp_client)
1435 {
1436         return g_strdup(dhcp_client->assigned_ip);
1437 }
1438
1439 char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
1440 {
1441         GList *option = NULL;
1442
1443         switch (dhcp_client->state) {
1444         case IPV4LL_DEFEND:
1445         case IPV4LL_MONITOR:
1446                 return g_strdup("255.255.0.0");
1447         case BOUND:
1448         case RENEWING:
1449         case REBINDING:
1450                 option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET);
1451                 if (option != NULL)
1452                         return g_strdup(option->data);
1453         case INIT_SELECTING:
1454         case REQUESTING:
1455         case RELEASED:
1456         case IPV4LL_PROBE:
1457         case IPV4LL_ANNOUNCE:
1458                 break;
1459         }
1460         return NULL;
1461 }
1462
1463 GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
1464                                                 unsigned char option_code)
1465 {
1466         if (g_list_find(dhcp_client->request_list,
1467                         GINT_TO_POINTER((int) option_code)) == NULL)
1468                 dhcp_client->request_list = g_list_prepend(
1469                                         dhcp_client->request_list,
1470                                         (GINT_TO_POINTER((int) option_code)));
1471
1472         return G_DHCP_CLIENT_ERROR_NONE;
1473 }
1474
1475 static uint8_t *alloc_dhcp_option(int code, const char *str, int extra)
1476 {
1477         uint8_t *storage;
1478         int len = strnlen(str, 255);
1479
1480         storage = malloc(len + extra + OPT_DATA);
1481         storage[OPT_CODE] = code;
1482         storage[OPT_LEN] = len + extra;
1483         memcpy(storage + extra + OPT_DATA, str, len);
1484
1485         return storage;
1486 }
1487
1488 /* Now only support send hostname */
1489 GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
1490                 unsigned char option_code, const char *option_value)
1491 {
1492         uint8_t *binary_option;
1493
1494         if (option_code == G_DHCP_HOST_NAME && option_value != NULL) {
1495                 binary_option = alloc_dhcp_option(option_code,
1496                                                         option_value, 0);
1497
1498                 g_hash_table_insert(dhcp_client->send_value_hash,
1499                         GINT_TO_POINTER((int) option_code), binary_option);
1500         }
1501
1502         return G_DHCP_CLIENT_ERROR_NONE;
1503 }
1504
1505 GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client)
1506 {
1507         if (dhcp_client == NULL)
1508                 return NULL;
1509
1510         __sync_fetch_and_add(&dhcp_client->ref_count, 1);
1511
1512         return dhcp_client;
1513 }
1514
1515 void g_dhcp_client_unref(GDHCPClient *dhcp_client)
1516 {
1517         if (dhcp_client == NULL)
1518                 return;
1519
1520         if (__sync_fetch_and_sub(&dhcp_client->ref_count, 1) != 1)
1521                 return;
1522
1523         g_dhcp_client_stop(dhcp_client);
1524
1525         g_free(dhcp_client->interface);
1526         g_free(dhcp_client->assigned_ip);
1527         g_free(dhcp_client->last_address);
1528
1529         g_list_free(dhcp_client->request_list);
1530         g_list_free(dhcp_client->require_list);
1531
1532         g_hash_table_destroy(dhcp_client->code_value_hash);
1533         g_hash_table_destroy(dhcp_client->send_value_hash);
1534
1535         g_free(dhcp_client);
1536 }
1537
1538 void g_dhcp_client_set_debug(GDHCPClient *dhcp_client,
1539                                 GDHCPDebugFunc func, gpointer user_data)
1540 {
1541         if (dhcp_client == NULL)
1542                 return;
1543
1544         dhcp_client->debug_func = func;
1545         dhcp_client->debug_data = user_data;
1546 }