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