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