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