1919af79355188c547ef8e28996a71cf70e64223
[framework/connectivity/connman.git] / gdhcp / client.c
1 /*
2  *
3  *  DHCP client library with GLib integration
4  *
5  *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/ioctl.h>
32 #include <arpa/inet.h>
33
34 #include <netpacket/packet.h>
35 #include <net/ethernet.h>
36 #include <net/if_arp.h>
37
38 #include <linux/if.h>
39 #include <linux/filter.h>
40
41 #include <glib.h>
42
43 #include "gdhcp.h"
44 #include "common.h"
45
46 #define DISCOVER_TIMEOUT 3
47 #define DISCOVER_RETRIES 5
48
49 #define REQUEST_TIMEOUT 3
50 #define REQUEST_RETRIES 5
51
52 typedef enum _listen_mode {
53         L_NONE,
54         L2,
55         L3,
56 } ListenMode;
57
58 typedef enum _dhcp_client_state {
59         INIT_SELECTING,
60         REQUESTING,
61         BOUND,
62         RENEWING,
63         REBINDING,
64         RELEASED,
65 } ClientState;
66
67 struct _GDHCPClient {
68         gint ref_count;
69         GDHCPType type;
70         ClientState state;
71         int ifindex;
72         char *interface;
73         uint8_t mac_address[6];
74         uint32_t xid;
75         uint32_t server_ip;
76         uint32_t requested_ip;
77         char *assigned_ip;
78         uint32_t lease_seconds;
79         ListenMode listen_mode;
80         int listener_sockfd;
81         uint8_t retry_times;
82         uint8_t ack_retry_times;
83         guint timeout;
84         guint listener_watch;
85         GIOChannel *listener_channel;
86         GList *require_list;
87         GList *request_list;
88         GHashTable *code_value_hash;
89         GHashTable *send_value_hash;
90         GDHCPClientEventFunc lease_available_cb;
91         gpointer lease_available_data;
92         GDHCPClientEventFunc no_lease_cb;
93         gpointer no_lease_data;
94         GDHCPClientEventFunc lease_lost_cb;
95         gpointer lease_lost_data;
96         GDHCPClientEventFunc address_conflict_cb;
97         gpointer address_conflict_data;
98         GDHCPDebugFunc debug_func;
99         gpointer debug_data;
100 };
101
102 static inline void debug(GDHCPClient *client, const char *format, ...)
103 {
104         char str[256];
105         va_list ap;
106
107         if (client->debug_func == NULL)
108                 return;
109
110         va_start(ap, format);
111
112         if (vsnprintf(str, sizeof(str), format, ap) > 0)
113                 client->debug_func(str, client->debug_data);
114
115         va_end(ap);
116 }
117
118 /* Initialize the packet with the proper defaults */
119 static void init_packet(GDHCPClient *dhcp_client,
120                 struct dhcp_packet *packet, char type)
121 {
122         dhcp_init_header(packet, type);
123
124         memcpy(packet->chaddr, dhcp_client->mac_address, 6);
125 }
126
127 static void add_request_options(GDHCPClient *dhcp_client,
128                                 struct dhcp_packet *packet)
129 {
130         int len = 0;
131         GList *list;
132         uint8_t code;
133         int end = dhcp_end_option(packet->options);
134
135         for (list = dhcp_client->request_list; list; list = list->next) {
136                 code = (uint8_t) GPOINTER_TO_INT(list->data);
137
138                 packet->options[end + OPT_DATA + len] = code;
139                 len++;
140         }
141
142         if (len) {
143                 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
144                 packet->options[end + OPT_LEN] = len;
145                 packet->options[end + OPT_DATA + len] = DHCP_END;
146         }
147 }
148
149 static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
150 {
151         uint8_t *option = value;
152         struct dhcp_packet *packet = user_data;
153
154         dhcp_add_binary_option(packet, option);
155 }
156
157 static void add_send_options(GDHCPClient *dhcp_client,
158                                 struct dhcp_packet *packet)
159 {
160         g_hash_table_foreach(dhcp_client->send_value_hash,
161                                 add_binary_option, packet);
162 }
163
164 static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
165 {
166         struct dhcp_packet packet;
167
168         debug(dhcp_client, "sending DHCP discover request");
169
170         init_packet(dhcp_client, &packet, DHCPDISCOVER);
171
172         packet.xid = dhcp_client->xid;
173
174         if (requested)
175                 dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
176
177         /* Explicitly saying that we want RFC-compliant packets helps
178          * some buggy DHCP servers to NOT send bigger packets */
179         dhcp_add_simple_option(&packet, DHCP_MAX_SIZE, htons(576));
180
181         add_request_options(dhcp_client, &packet);
182
183         add_send_options(dhcp_client, &packet);
184
185         return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
186                                         INADDR_BROADCAST, SERVER_PORT,
187                                         MAC_BCAST_ADDR, dhcp_client->ifindex);
188 }
189
190 static int send_select(GDHCPClient *dhcp_client)
191 {
192         struct dhcp_packet packet;
193         struct in_addr addr;
194
195         debug(dhcp_client, "sending DHCP select request");
196
197         init_packet(dhcp_client, &packet, DHCPREQUEST);
198
199         packet.xid = dhcp_client->xid;
200
201         dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP,
202                                         dhcp_client->requested_ip);
203         dhcp_add_simple_option(&packet, DHCP_SERVER_ID, dhcp_client->server_ip);
204
205         add_request_options(dhcp_client, &packet);
206
207         add_send_options(dhcp_client, &packet);
208
209         addr.s_addr = dhcp_client->requested_ip;
210
211         return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
212                                         INADDR_BROADCAST, SERVER_PORT,
213                                         MAC_BCAST_ADDR, dhcp_client->ifindex);
214 }
215
216 static int send_renew(GDHCPClient *dhcp_client)
217 {
218         struct dhcp_packet packet;
219
220         debug(dhcp_client, "sending DHCP renew request");
221
222         init_packet(dhcp_client , &packet, DHCPREQUEST);
223         packet.xid = dhcp_client->xid;
224         packet.ciaddr = dhcp_client->requested_ip;
225
226         add_request_options(dhcp_client, &packet);
227
228         add_send_options(dhcp_client, &packet);
229
230         return dhcp_send_kernel_packet(&packet,
231                 dhcp_client->requested_ip, CLIENT_PORT,
232                 dhcp_client->server_ip, SERVER_PORT);
233 }
234
235 static int send_rebound(GDHCPClient *dhcp_client)
236 {
237         struct dhcp_packet packet;
238
239         debug(dhcp_client, "sending DHCP rebound request");
240
241         init_packet(dhcp_client , &packet, DHCPREQUEST);
242         packet.xid = dhcp_client->xid;
243         packet.ciaddr = dhcp_client->requested_ip;
244
245         add_request_options(dhcp_client, &packet);
246
247         add_send_options(dhcp_client, &packet);
248
249         return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
250                                         INADDR_BROADCAST, SERVER_PORT,
251                                         MAC_BCAST_ADDR, dhcp_client->ifindex);
252 }
253
254 static int send_release(GDHCPClient *dhcp_client,
255                         uint32_t server, uint32_t ciaddr)
256 {
257         struct dhcp_packet packet;
258
259         debug(dhcp_client, "sending DHCP release request");
260
261         init_packet(dhcp_client, &packet, DHCPRELEASE);
262         packet.xid = rand();
263         packet.ciaddr = ciaddr;
264
265         dhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
266
267         return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
268                                                 server, SERVER_PORT);
269 }
270
271 static gboolean interface_is_up(int index)
272 {
273         int sk, err;
274         struct ifreq ifr;
275         gboolean ret = FALSE;
276
277         sk = socket(PF_INET, SOCK_DGRAM, 0);
278         if (sk < 0) {
279                 perror("Open socket error");
280                 return FALSE;
281         }
282
283         memset(&ifr, 0, sizeof(ifr));
284         ifr.ifr_ifindex = index;
285
286         err = ioctl(sk, SIOCGIFNAME, &ifr);
287         if (err < 0) {
288                 perror("Get interface name error");
289                 goto done;
290         }
291
292         err = ioctl(sk, SIOCGIFFLAGS, &ifr);
293         if (err < 0) {
294                 perror("Get interface flags error");
295                 goto done;
296         }
297
298         if (ifr.ifr_flags & IFF_UP)
299                 ret = TRUE;
300
301 done:
302         close(sk);
303
304         return ret;
305 }
306
307 static char *get_interface_name(int index)
308 {
309         struct ifreq ifr;
310         int sk, err;
311
312         if (index < 0)
313                 return NULL;
314
315         sk = socket(PF_INET, SOCK_DGRAM, 0);
316         if (sk < 0) {
317                 perror("Open socket error");
318                 return NULL;
319         }
320
321         memset(&ifr, 0, sizeof(ifr));
322         ifr.ifr_ifindex = index;
323
324         err = ioctl(sk, SIOCGIFNAME, &ifr);
325         if (err < 0) {
326                 perror("Get interface name error");
327                 close(sk);
328                 return NULL;
329         }
330
331         close(sk);
332
333         return g_strdup(ifr.ifr_name);
334 }
335
336 static void get_interface_mac_address(int index, uint8_t *mac_address)
337 {
338         struct ifreq ifr;
339         int sk, err;
340
341         sk = socket(PF_INET, SOCK_DGRAM, 0);
342         if (sk < 0) {
343                 perror("Open socket error");
344                 return;
345         }
346
347         memset(&ifr, 0, sizeof(ifr));
348         ifr.ifr_ifindex = index;
349
350         err = ioctl(sk, SIOCGIFNAME, &ifr);
351         if (err < 0) {
352                 perror("Get interface name error");
353                 goto done;
354         }
355
356         err = ioctl(sk, SIOCGIFHWADDR, &ifr);
357         if (err < 0) {
358                 perror("Get mac address error");
359                 goto done;
360         }
361
362         memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
363
364 done:
365         close(sk);
366 }
367
368 static void remove_value(gpointer data, gpointer user_data)
369 {
370         char *value = data;
371         g_free(value);
372 }
373
374 static void remove_option_value(gpointer data)
375 {
376         GList *option_value = data;
377
378         g_list_foreach(option_value, remove_value, NULL);
379 }
380
381 GDHCPClient *g_dhcp_client_new(GDHCPType type,
382                         int ifindex, GDHCPClientError *error)
383 {
384         GDHCPClient *dhcp_client;
385
386         if (ifindex < 0) {
387                 *error = G_DHCP_CLIENT_ERROR_INVALID_INDEX;
388                 return NULL;
389         }
390
391         dhcp_client = g_try_new0(GDHCPClient, 1);
392         if (dhcp_client == NULL) {
393                 *error = G_DHCP_CLIENT_ERROR_NOMEM;
394                 return NULL;
395         }
396
397         dhcp_client->interface = get_interface_name(ifindex);
398         if (dhcp_client->interface == NULL) {
399                 *error = G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE;
400                 goto error;
401         }
402
403         if (interface_is_up(ifindex) == FALSE) {
404                 *error = G_DHCP_CLIENT_ERROR_INTERFACE_DOWN;
405                 goto error;
406         }
407
408         get_interface_mac_address(ifindex, dhcp_client->mac_address);
409
410         dhcp_client->listener_sockfd = -1;
411         dhcp_client->listener_channel = NULL;
412         dhcp_client->listen_mode = L_NONE;
413         dhcp_client->ref_count = 1;
414         dhcp_client->type = type;
415         dhcp_client->ifindex = ifindex;
416         dhcp_client->lease_available_cb = NULL;
417         dhcp_client->no_lease_cb = NULL;
418         dhcp_client->lease_lost_cb = NULL;
419         dhcp_client->address_conflict_cb = NULL;
420         dhcp_client->listener_watch = 0;
421         dhcp_client->retry_times = 0;
422         dhcp_client->ack_retry_times = 0;
423         dhcp_client->code_value_hash = g_hash_table_new_full(g_direct_hash,
424                                 g_direct_equal, NULL, remove_option_value);
425         dhcp_client->send_value_hash = g_hash_table_new_full(g_direct_hash,
426                                 g_direct_equal, NULL, g_free);
427         dhcp_client->request_list = NULL;
428         dhcp_client->require_list = NULL;
429
430         *error = G_DHCP_CLIENT_ERROR_NONE;
431
432         return dhcp_client;
433
434 error:
435         g_free(dhcp_client->interface);
436         g_free(dhcp_client);
437         return NULL;
438 }
439
440 #define SERVER_AND_CLIENT_PORTS  ((67 << 16) + 68)
441
442 static int dhcp_l2_socket(int ifindex)
443 {
444         int fd;
445         struct sockaddr_ll sock;
446
447         /*
448          * Comment:
449          *
450          *      I've selected not to see LL header, so BPF doesn't see it, too.
451          *      The filter may also pass non-IP and non-ARP packets, but we do
452          *      a more complete check when receiving the message in userspace.
453          *
454          * and filter shamelessly stolen from:
455          *
456          *      http://www.flamewarmaster.de/software/dhcpclient/
457          *
458          * There are a few other interesting ideas on that page (look under
459          * "Motivation").  Use of netlink events is most interesting.  Think
460          * of various network servers listening for events and reconfiguring.
461          * That would obsolete sending HUP signals and/or make use of restarts.
462          *
463          * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
464          * License: GPL v2.
465          *
466          * TODO: make conditional?
467          */
468         static const struct sock_filter filter_instr[] = {
469                 /* check for udp */
470                 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
471                 /* L5, L1, is UDP? */
472                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0),
473                 /* ugly check for arp on ethernet-like and IPv4 */
474                 BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */
475                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4),/* L3, L4 */
476                 /* skip IP header */
477                 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */
478                 /* check udp source and destination ports */
479                 BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
480                 /* L3, L4 */
481                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),
482                 /* returns */
483                 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* L3: pass */
484                 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
485         };
486
487         static const struct sock_fprog filter_prog = {
488                 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
489                 /* casting const away: */
490                 .filter = (struct sock_filter *) filter_instr,
491         };
492
493         fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
494         if (fd < 0)
495                 return fd;
496
497         if (SERVER_PORT == 67 && CLIENT_PORT == 68)
498                 /* Use only if standard ports are in use */
499                 setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
500                                                         sizeof(filter_prog));
501
502         sock.sll_family = AF_PACKET;
503         sock.sll_protocol = htons(ETH_P_IP);
504         sock.sll_ifindex = ifindex;
505
506         if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
507                 close(fd);
508                 return -errno;
509         }
510
511         return fd;
512 }
513
514 static gboolean sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
515 {
516         if (packet->ip.protocol != IPPROTO_UDP)
517                 return FALSE;
518
519         if (packet->ip.version != IPVERSION)
520                 return FALSE;
521
522         if (packet->ip.ihl != sizeof(packet->ip) >> 2)
523                 return FALSE;
524
525         if (packet->udp.dest != htons(CLIENT_PORT))
526                 return FALSE;
527
528         if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip)))
529                 return FALSE;
530
531         return TRUE;
532 }
533
534 static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
535 {
536         int bytes;
537         struct ip_udp_dhcp_packet packet;
538         uint16_t check;
539
540         memset(&packet, 0, sizeof(packet));
541
542         bytes = read(fd, &packet, sizeof(packet));
543         if (bytes < 0)
544                 return -1;
545
546         if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
547                 return -1;
548
549         if (bytes < ntohs(packet.ip.tot_len))
550                 /* packet is bigger than sizeof(packet), we did partial read */
551                 return -1;
552
553         /* ignore any extra garbage bytes */
554         bytes = ntohs(packet.ip.tot_len);
555
556         if (sanity_check(&packet, bytes) == FALSE)
557                 return -1;
558
559         check = packet.ip.check;
560         packet.ip.check = 0;
561         if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip)))
562                 return -1;
563
564         /* verify UDP checksum. IP header has to be modified for this */
565         memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
566         /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
567         packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
568         check = packet.udp.check;
569         packet.udp.check = 0;
570         if (check && check != dhcp_checksum(&packet, bytes))
571                 return -1;
572
573         memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) +
574                                                         sizeof(packet.udp)));
575
576         if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
577                 return -1;
578
579         return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
580 }
581
582 static gboolean check_package_owner(GDHCPClient *dhcp_client,
583                                         struct dhcp_packet *packet)
584 {
585         if (packet->xid != dhcp_client->xid)
586                 return FALSE;
587
588         if (packet->hlen != 6)
589                 return FALSE;
590
591         if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
592                 return FALSE;
593
594         return TRUE;
595 }
596
597 static void start_request(GDHCPClient *dhcp_client);
598
599 static gboolean request_timeout(gpointer user_data)
600 {
601         GDHCPClient *dhcp_client = user_data;
602
603         debug(dhcp_client, "request timeout (retries %d)",
604                                         dhcp_client->retry_times);
605
606         dhcp_client->retry_times++;
607
608         start_request(dhcp_client);
609
610         return FALSE;
611 }
612
613 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
614                                                         gpointer user_data);
615
616 static int switch_listening_mode(GDHCPClient *dhcp_client,
617                                         ListenMode listen_mode)
618 {
619         GIOChannel *listener_channel;
620         int listener_sockfd;
621
622         debug(dhcp_client, "switch listening mode (%d ==> %d)",
623                                 dhcp_client->listen_mode, listen_mode);
624
625         if (dhcp_client->listen_mode == listen_mode)
626                 return 0;
627
628         if (dhcp_client->listen_mode != L_NONE) {
629                 g_source_remove(dhcp_client->listener_watch);
630                 dhcp_client->listener_channel = NULL;
631                 dhcp_client->listen_mode = L_NONE;
632                 dhcp_client->listener_sockfd = -1;
633                 dhcp_client->listener_watch = 0;
634         }
635
636         if (listen_mode == L_NONE)
637                 return 0;
638
639         if (listen_mode == L2)
640                 listener_sockfd = dhcp_l2_socket(dhcp_client->ifindex);
641         else if (listen_mode == L3)
642                 listener_sockfd = dhcp_l3_socket(CLIENT_PORT,
643                                                 dhcp_client->interface);
644         else
645                 return -EIO;
646
647         if (listener_sockfd < 0)
648                 return -EIO;
649
650         listener_channel = g_io_channel_unix_new(listener_sockfd);
651         if (listener_channel == NULL) {
652                 /* Failed to create listener channel */
653                 close(listener_sockfd);
654                 return -EIO;
655         }
656
657         dhcp_client->listen_mode = listen_mode;
658         dhcp_client->listener_sockfd = listener_sockfd;
659         dhcp_client->listener_channel = listener_channel;
660
661         g_io_channel_set_close_on_unref(listener_channel, TRUE);
662         dhcp_client->listener_watch =
663                         g_io_add_watch_full(listener_channel,
664                                                 G_PRIORITY_HIGH, G_IO_IN,
665                                                 listener_event, dhcp_client,
666                                                                 NULL);
667         g_io_channel_unref(dhcp_client->listener_channel);
668
669         return 0;
670 }
671
672 static void start_request(GDHCPClient *dhcp_client)
673 {
674         debug(dhcp_client, "start request (retries %d)",
675                                         dhcp_client->retry_times);
676
677         if (dhcp_client->retry_times == REQUEST_RETRIES) {
678                 dhcp_client->state = INIT_SELECTING;
679
680                 if (dhcp_client->no_lease_cb != NULL)
681                         dhcp_client->no_lease_cb(dhcp_client,
682                                         dhcp_client->no_lease_data);
683
684                 return;
685         }
686
687         if (dhcp_client->retry_times == 0) {
688                 dhcp_client->state = REQUESTING;
689                 switch_listening_mode(dhcp_client, L2);
690         }
691
692         send_select(dhcp_client);
693
694         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
695                                                         REQUEST_TIMEOUT,
696                                                         request_timeout,
697                                                         dhcp_client,
698                                                         NULL);
699 }
700
701 static uint32_t get_lease(struct dhcp_packet *packet)
702 {
703         uint8_t *option_u8;
704         uint32_t lease_seconds;
705
706         option_u8 = dhcp_get_option(packet, DHCP_LEASE_TIME);
707         if (option_u8 == NULL)
708                 return 3600;
709
710         lease_seconds = dhcp_get_unaligned((uint32_t *) option_u8);
711         lease_seconds = ntohl(lease_seconds);
712         /* paranoia: must not be prone to overflows */
713         lease_seconds &= 0x0fffffff;
714         if (lease_seconds < 10)
715                 lease_seconds = 10;
716
717         return lease_seconds;
718 }
719
720 static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times)
721 {
722         debug(dhcp_client, "restart DHCP (retries %d)", retry_times);
723
724         if (dhcp_client->timeout > 0) {
725                 g_source_remove(dhcp_client->timeout);
726                 dhcp_client->timeout = 0;
727         }
728
729         dhcp_client->retry_times = retry_times;
730         dhcp_client->requested_ip = 0;
731         switch_listening_mode(dhcp_client, L2);
732
733         g_dhcp_client_start(dhcp_client);
734 }
735
736 static gboolean start_rebound_timeout(gpointer user_data)
737 {
738         GDHCPClient *dhcp_client = user_data;
739
740         debug(dhcp_client, "start rebound timeout");
741
742         switch_listening_mode(dhcp_client, L2);
743
744         dhcp_client->lease_seconds >>= 1;
745
746         /* We need to have enough time to receive ACK package*/
747         if (dhcp_client->lease_seconds <= 6) {
748
749                 /* ip need to be cleared */
750                 if (dhcp_client->lease_lost_cb != NULL)
751                         dhcp_client->lease_lost_cb(dhcp_client,
752                                         dhcp_client->lease_lost_data);
753
754                 restart_dhcp(dhcp_client, 0);
755         } else {
756                 send_rebound(dhcp_client);
757
758                 dhcp_client->timeout =
759                                 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
760                                                 dhcp_client->lease_seconds >> 1,
761                                                         start_rebound_timeout,
762                                                                 dhcp_client,
763                                                                 NULL);
764         }
765
766         return FALSE;
767 }
768
769 static void start_rebound(GDHCPClient *dhcp_client)
770 {
771         debug(dhcp_client, "start rebound");
772
773         dhcp_client->state = REBINDING;
774
775         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
776                                                 dhcp_client->lease_seconds >> 1,
777                                                         start_rebound_timeout,
778                                                                 dhcp_client,
779                                                                 NULL);
780 }
781
782 static gboolean start_renew_timeout(gpointer user_data)
783 {
784         GDHCPClient *dhcp_client = user_data;
785
786         debug(dhcp_client, "start renew timeout");
787
788         dhcp_client->state = RENEWING;
789
790         dhcp_client->lease_seconds >>= 1;
791
792         switch_listening_mode(dhcp_client, L3);
793         if (dhcp_client->lease_seconds <= 60)
794                 start_rebound(dhcp_client);
795         else {
796                 send_renew(dhcp_client);
797
798                 dhcp_client->timeout =
799                                 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
800                                                 dhcp_client->lease_seconds >> 1,
801                                                         start_renew_timeout,
802                                                                 dhcp_client,
803                                                                 NULL);
804         }
805
806         return FALSE;
807 }
808
809 static void start_bound(GDHCPClient *dhcp_client)
810 {
811         debug(dhcp_client, "start bound");
812
813         dhcp_client->state = BOUND;
814
815         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
816                                         dhcp_client->lease_seconds >> 1,
817                                         start_renew_timeout, dhcp_client,
818                                                         NULL);
819 }
820
821 static gboolean restart_dhcp_timeout(gpointer user_data)
822 {
823         GDHCPClient *dhcp_client = user_data;
824
825         debug(dhcp_client, "restart DHCP timeout");
826
827         dhcp_client->ack_retry_times++;
828
829         restart_dhcp(dhcp_client, dhcp_client->ack_retry_times);
830
831         return FALSE;
832 }
833
834 static char *get_ip(uint32_t ip)
835 {
836         struct in_addr addr;
837
838         addr.s_addr = ip;
839
840         return g_strdup(inet_ntoa(addr));
841 }
842
843 /* get a rough idea of how long an option will be */
844 static const uint8_t len_of_option_as_string[] = {
845         [OPTION_IP] = sizeof("255.255.255.255 "),
846         [OPTION_STRING] = 1,
847         [OPTION_U8] = sizeof("255 "),
848         [OPTION_U16] = sizeof("65535 "),
849         [OPTION_U32] = sizeof("4294967295 "),
850 };
851
852 static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
853 {
854         return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
855 }
856
857 /* Create "opt_value1 option_value2 ..." string */
858 static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
859 {
860         unsigned upper_length;
861         int len, optlen;
862         char *dest, *ret;
863
864         len = option[OPT_LEN - OPT_DATA];
865         type &= OPTION_TYPE_MASK;
866         optlen = dhcp_option_lengths[type];
867         if (optlen == 0)
868                 return NULL;
869         upper_length = len_of_option_as_string[type] *
870                         ((unsigned)len / (unsigned)optlen);
871         dest = ret = malloc(upper_length + 1);
872         if (ret == NULL)
873                 return NULL;
874
875         while (len >= optlen) {
876                 switch (type) {
877                 case OPTION_IP:
878                         dest += sprint_nip(dest, "", option);
879                         break;
880                 case OPTION_U16: {
881                         uint16_t val_u16 = dhcp_get_unaligned(
882                                                 (uint16_t *) option);
883                         dest += sprintf(dest, "%u", ntohs(val_u16));
884                         break;
885                 }
886                 case OPTION_U32: {
887                         uint32_t val_u32 = dhcp_get_unaligned(
888                                                 (uint32_t *) option);
889                         dest += sprintf(dest, type == OPTION_U32 ? "%lu" :
890                                         "%ld", (unsigned long) ntohl(val_u32));
891                         break;
892                 }
893                 case OPTION_STRING:
894                         memcpy(dest, option, len);
895                         dest[len] = '\0';
896                         return ret;
897                 default:
898                         break;
899                 }
900                 option += optlen;
901                 len -= optlen;
902                 if (len <= 0)
903                         break;
904                 *dest++ = ' ';
905                 *dest = '\0';
906         }
907
908         return ret;
909 }
910
911 static GList *get_option_value_list(char *value)
912 {
913         char *pos = value;
914         GList *list = NULL;
915
916         if (pos == NULL)
917                 return NULL;
918
919         while ((pos = strchr(pos, ' ')) != NULL) {
920                 *pos = '\0';
921
922                 list =  g_list_append(list, g_strdup(value));
923
924                 value = ++pos;
925         }
926
927         list =  g_list_append(list, g_strdup(value));
928
929         return list;
930 }
931
932 static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
933 {
934         GDHCPOptionType type;
935         GList *list, *value_list;
936         char *option_value;
937         uint8_t *option;
938         uint8_t code;
939
940         for (list = dhcp_client->request_list; list; list = list->next) {
941                 code = (uint8_t) GPOINTER_TO_INT(list->data);
942
943                 option = dhcp_get_option(packet, code);
944                 if (option == NULL) {
945                         g_hash_table_remove(dhcp_client->code_value_hash,
946                                                 GINT_TO_POINTER((int) code));
947                         continue;
948                 }
949
950                 type =  dhcp_get_code_type(code);
951
952                 option_value = malloc_option_value_string(option, type);
953                 if (option_value == NULL)
954                         g_hash_table_remove(dhcp_client->code_value_hash,
955                                                 GINT_TO_POINTER((int) code));
956
957                 value_list = get_option_value_list(option_value);
958
959                 g_free(option_value);
960
961                 if (value_list == NULL)
962                         g_hash_table_remove(dhcp_client->code_value_hash,
963                                                 GINT_TO_POINTER((int) code));
964                 else
965                         g_hash_table_insert(dhcp_client->code_value_hash,
966                                 GINT_TO_POINTER((int) code), value_list);
967         }
968 }
969
970 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
971                                                         gpointer user_data)
972 {
973         GDHCPClient *dhcp_client = user_data;
974         struct dhcp_packet packet;
975         uint8_t *message_type, *option_u8;
976         int re;
977
978         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
979                 dhcp_client->listener_watch = 0;
980                 return FALSE;
981         }
982
983         if (dhcp_client->listen_mode == L_NONE)
984                 return FALSE;
985
986         if (dhcp_client->listen_mode == L2)
987                 re = dhcp_recv_l2_packet(&packet, dhcp_client->listener_sockfd);
988         else if (dhcp_client->listen_mode == L3)
989                 re = dhcp_recv_l3_packet(&packet, dhcp_client->listener_sockfd);
990         else
991                 re = -EIO;
992
993         if (re < 0)
994                 return TRUE;
995
996         if (check_package_owner(dhcp_client, &packet) == FALSE)
997                 return TRUE;
998
999         message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
1000         if (message_type == NULL)
1001                 /* No message type option, ignore pakcage */
1002                 return TRUE;
1003
1004         debug(dhcp_client, "received DHCP packet (current state %d)",
1005                                                         dhcp_client->state);
1006
1007         switch (dhcp_client->state) {
1008         case INIT_SELECTING:
1009                 if (*message_type != DHCPOFFER)
1010                         return TRUE;
1011
1012                 g_source_remove(dhcp_client->timeout);
1013                 dhcp_client->timeout = 0;
1014                 dhcp_client->retry_times = 0;
1015
1016                 option_u8 = dhcp_get_option(&packet, DHCP_SERVER_ID);
1017                 dhcp_client->server_ip =
1018                                 dhcp_get_unaligned((uint32_t *) option_u8);
1019                 dhcp_client->requested_ip = packet.yiaddr;
1020
1021                 dhcp_client->state = REQUESTING;
1022
1023                 start_request(dhcp_client);
1024
1025                 return TRUE;
1026         case REQUESTING:
1027         case RENEWING:
1028         case REBINDING:
1029                 if (*message_type == DHCPACK) {
1030                         dhcp_client->retry_times = 0;
1031
1032                         if (dhcp_client->timeout > 0)
1033                                 g_source_remove(dhcp_client->timeout);
1034                         dhcp_client->timeout = 0;
1035
1036                         dhcp_client->lease_seconds = get_lease(&packet);
1037
1038                         get_request(dhcp_client, &packet);
1039
1040                         switch_listening_mode(dhcp_client, L_NONE);
1041
1042                         g_free(dhcp_client->assigned_ip);
1043                         dhcp_client->assigned_ip = get_ip(packet.yiaddr);
1044
1045                         /* Address should be set up here */
1046                         if (dhcp_client->lease_available_cb != NULL)
1047                                 dhcp_client->lease_available_cb(dhcp_client,
1048                                         dhcp_client->lease_available_data);
1049
1050                         start_bound(dhcp_client);
1051                 } else if (*message_type == DHCPNAK) {
1052                         dhcp_client->retry_times = 0;
1053
1054                         if (dhcp_client->timeout > 0)
1055                                 g_source_remove(dhcp_client->timeout);
1056
1057                         dhcp_client->timeout = g_timeout_add_seconds_full(
1058                                                         G_PRIORITY_HIGH, 3,
1059                                                         restart_dhcp_timeout,
1060                                                         dhcp_client,
1061                                                         NULL);
1062                 }
1063
1064                 break;
1065         default:
1066                 break;
1067         }
1068
1069         debug(dhcp_client, "processed DHCP packet (new state %d)",
1070                                                         dhcp_client->state);
1071
1072         return TRUE;
1073 }
1074
1075 static gboolean discover_timeout(gpointer user_data)
1076 {
1077         GDHCPClient *dhcp_client = user_data;
1078
1079         dhcp_client->retry_times++;
1080
1081         g_dhcp_client_start(dhcp_client);
1082
1083         return FALSE;
1084 }
1085
1086 int g_dhcp_client_start(GDHCPClient *dhcp_client)
1087 {
1088         int re;
1089
1090         if (dhcp_client->retry_times == DISCOVER_RETRIES) {
1091                 if (dhcp_client->no_lease_cb != NULL)
1092                         dhcp_client->no_lease_cb(dhcp_client,
1093                                         dhcp_client->no_lease_data);
1094
1095                 return 0;
1096         }
1097
1098         if (dhcp_client->retry_times == 0) {
1099                 g_free(dhcp_client->assigned_ip);
1100                 dhcp_client->assigned_ip = NULL;
1101
1102                 dhcp_client->state = INIT_SELECTING;
1103                 re = switch_listening_mode(dhcp_client, L2);
1104                 if (re != 0)
1105                         return re;
1106
1107                 dhcp_client->xid = rand();
1108         }
1109
1110         send_discover(dhcp_client, 0);
1111
1112         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1113                                                         DISCOVER_TIMEOUT,
1114                                                         discover_timeout,
1115                                                         dhcp_client,
1116                                                         NULL);
1117         return 0;
1118 }
1119
1120 void g_dhcp_client_stop(GDHCPClient *dhcp_client)
1121 {
1122         switch_listening_mode(dhcp_client, L_NONE);
1123
1124         if (dhcp_client->state == BOUND ||
1125                         dhcp_client->state == RENEWING ||
1126                                 dhcp_client->state == REBINDING)
1127                 send_release(dhcp_client, dhcp_client->server_ip,
1128                                         dhcp_client->requested_ip);
1129
1130         if (dhcp_client->timeout > 0) {
1131                 g_source_remove(dhcp_client->timeout);
1132                 dhcp_client->timeout = 0;
1133         }
1134
1135         if (dhcp_client->listener_watch > 0) {
1136                 g_source_remove(dhcp_client->listener_watch);
1137                 dhcp_client->listener_watch = 0;
1138         }
1139
1140         dhcp_client->listener_channel = NULL;
1141
1142         dhcp_client->retry_times = 0;
1143         dhcp_client->ack_retry_times = 0;
1144
1145         dhcp_client->requested_ip = 0;
1146         dhcp_client->state = RELEASED;
1147         dhcp_client->lease_seconds = 0;
1148 }
1149
1150 GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
1151                                         unsigned char option_code)
1152 {
1153         return g_hash_table_lookup(dhcp_client->code_value_hash,
1154                                         GINT_TO_POINTER((int) option_code));
1155 }
1156
1157 void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
1158                                         GDHCPClientEvent event,
1159                                         GDHCPClientEventFunc func,
1160                                                         gpointer data)
1161 {
1162         switch (event) {
1163         case G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE:
1164                 dhcp_client->lease_available_cb = func;
1165                 dhcp_client->lease_available_data = data;
1166                 return;
1167         case G_DHCP_CLIENT_EVENT_NO_LEASE:
1168                 dhcp_client->no_lease_cb = func;
1169                 dhcp_client->no_lease_data = data;
1170                 return;
1171         case G_DHCP_CLIENT_EVENT_LEASE_LOST:
1172                 dhcp_client->lease_lost_cb = func;
1173                 dhcp_client->lease_lost_data = data;
1174                 return;
1175         case G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT:
1176                 dhcp_client->address_conflict_cb = func;
1177                 dhcp_client->address_conflict_data = data;
1178                 return;
1179         }
1180 }
1181
1182 int g_dhcp_client_get_index(GDHCPClient *dhcp_client)
1183 {
1184         return dhcp_client->ifindex;
1185 }
1186
1187 char *g_dhcp_client_get_address(GDHCPClient *dhcp_client)
1188 {
1189         return g_strdup(dhcp_client->assigned_ip);
1190 }
1191
1192 GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
1193                                                 unsigned char option_code)
1194 {
1195         if (g_list_find(dhcp_client->request_list,
1196                         GINT_TO_POINTER((int) option_code)) == NULL)
1197                 dhcp_client->request_list = g_list_prepend(
1198                                         dhcp_client->request_list,
1199                                         (GINT_TO_POINTER((int) option_code)));
1200
1201         return G_DHCP_CLIENT_ERROR_NONE;
1202 }
1203
1204 static uint8_t *alloc_dhcp_option(int code, const char *str, int extra)
1205 {
1206         uint8_t *storage;
1207         int len = strnlen(str, 255);
1208
1209         storage = malloc(len + extra + OPT_DATA);
1210         storage[OPT_CODE] = code;
1211         storage[OPT_LEN] = len + extra;
1212         memcpy(storage + extra + OPT_DATA, str, len);
1213
1214         return storage;
1215 }
1216
1217 /* Now only support send hostname */
1218 GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
1219                 unsigned char option_code, const char *option_value)
1220 {
1221         uint8_t *binary_option;
1222
1223         if (option_code == G_DHCP_HOST_NAME && option_value != NULL) {
1224                 binary_option = alloc_dhcp_option(option_code,
1225                                                         option_value, 0);
1226
1227                 g_hash_table_insert(dhcp_client->send_value_hash,
1228                         GINT_TO_POINTER((int) option_code), binary_option);
1229         }
1230
1231         return G_DHCP_CLIENT_ERROR_NONE;
1232 }
1233
1234 GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client)
1235 {
1236         if (dhcp_client == NULL)
1237                 return NULL;
1238
1239         g_atomic_int_inc(&dhcp_client->ref_count);
1240
1241         return dhcp_client;
1242 }
1243
1244 void g_dhcp_client_unref(GDHCPClient *dhcp_client)
1245 {
1246         if (dhcp_client == NULL)
1247                 return;
1248
1249         if (g_atomic_int_dec_and_test(&dhcp_client->ref_count) == FALSE)
1250                 return;
1251
1252         g_dhcp_client_stop(dhcp_client);
1253
1254         g_free(dhcp_client->interface);
1255         g_free(dhcp_client->assigned_ip);
1256
1257         g_list_free(dhcp_client->request_list);
1258         g_list_free(dhcp_client->require_list);
1259
1260         g_hash_table_destroy(dhcp_client->code_value_hash);
1261         g_hash_table_destroy(dhcp_client->send_value_hash);
1262
1263         g_free(dhcp_client);
1264 }
1265
1266 void g_dhcp_client_set_debug(GDHCPClient *dhcp_client,
1267                                 GDHCPDebugFunc func, gpointer user_data)
1268 {
1269         if (dhcp_client == NULL)
1270                 return;
1271
1272         dhcp_client->debug_func = func;
1273         dhcp_client->debug_data = user_data;
1274 }