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