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