DA: Add DHCP state notify function
[platform/upstream/connman.git] / src / dhcp.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2013  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 #include <stdio.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <net/ethernet.h>
30
31 #ifndef IPV6_MIN_MTU
32 #define IPV6_MIN_MTU 1280
33 #endif
34
35 #include <connman/ipconfig.h>
36 #include <include/setting.h>
37
38 #include <gdhcp/gdhcp.h>
39
40 #include <glib.h>
41
42 #include "connman.h"
43
44 #define RATE_LIMIT_INTERVAL     60      /* delay between successive attempts */
45
46 #if defined TIZEN_EXT
47 #define DHCP_SUCCESS            "DHCP_SUCCESS"
48 #endif
49
50 struct connman_dhcp {
51         struct connman_ipconfig *ipconfig;
52         struct connman_network *network;
53         dhcp_cb callback;
54         gpointer user_data;
55
56         char **nameservers;
57         char **timeservers;
58         char *pac;
59
60         unsigned int timeout;
61
62         GDHCPClient *ipv4ll_client;
63         GDHCPClient *dhcp_client;
64         char *ipv4ll_debug_prefix;
65         char *dhcp_debug_prefix;
66
67         bool ipv4ll_running;
68 };
69
70 static GHashTable *ipconfig_table;
71
72 static void dhcp_free(struct connman_dhcp *dhcp)
73 {
74 #if defined TIZEN_EXT
75         DBG("dhcp_free [%p]", dhcp);
76 #endif
77         g_strfreev(dhcp->nameservers);
78         g_strfreev(dhcp->timeservers);
79         g_free(dhcp->pac);
80
81         dhcp->nameservers = NULL;
82         dhcp->timeservers = NULL;
83         dhcp->pac = NULL;
84
85         g_free(dhcp);
86 #if defined TIZEN_EXT
87         dhcp = NULL;
88 #endif
89 }
90
91 static void ipv4ll_stop_client(struct connman_dhcp *dhcp)
92 {
93 #if defined TIZEN_EXT
94         DBG("dhcp [%p] ipv4ll_client [%p]", dhcp, dhcp->ipv4ll_client);
95 #endif
96         if (!dhcp->ipv4ll_client)
97                 return;
98
99         g_dhcp_client_stop(dhcp->ipv4ll_client);
100         g_dhcp_client_unref(dhcp->ipv4ll_client);
101         dhcp->ipv4ll_client = NULL;
102         dhcp->ipv4ll_running = false;
103
104         g_free(dhcp->ipv4ll_debug_prefix);
105         dhcp->ipv4ll_debug_prefix = NULL;
106 }
107
108 static bool apply_dhcp_invalidate_on_network(struct connman_dhcp *dhcp)
109 {
110         struct connman_service *service;
111         int i;
112
113         if (!dhcp->network)
114                 return true;
115
116         service = connman_service_lookup_from_network(dhcp->network);
117         if (!service) {
118                 connman_error("Can not lookup service");
119                 return false;
120         }
121
122         __connman_service_set_domainname(service, NULL);
123         __connman_ipconfig_set_proxy_autoconfig(dhcp->ipconfig, NULL);
124
125         if (dhcp->timeservers) {
126                 for (i = 0; dhcp->timeservers[i]; i++) {
127                         __connman_service_timeserver_remove(service,
128                                                         dhcp->timeservers[i]);
129                 }
130                 g_strfreev(dhcp->timeservers);
131                 dhcp->timeservers = NULL;
132         }
133         if (dhcp->nameservers) {
134                 for (i = 0; dhcp->nameservers[i]; i++) {
135 #if defined TIZEN_EXT
136                         __connman_service_nameserver_remove(service,
137                                         dhcp->nameservers[i], false,
138                                         CONNMAN_IPCONFIG_TYPE_IPV4);
139 #else
140                         __connman_service_nameserver_remove(service,
141                                                 dhcp->nameservers[i], false);
142 #endif
143                 }
144                 g_strfreev(dhcp->nameservers);
145                 dhcp->nameservers = NULL;
146         }
147
148         return true;
149 }
150
151 /**
152  * dhcp_invalidate: Invalidate an existing DHCP lease
153  * @dhcp: pointer to the DHCP lease to invalidate.
154  * @callback: flag indicating whether or not to invoke the client callback
155  *            if present.
156  *
157  * Invalidates an existing DHCP lease, optionally invoking the client
158  * callback. The caller may wish to avoid the client callback invocation
159  * when the invocation of that callback might otherwise unnecessarily upset
160  * service state due to the IP configuration change implied by this
161  * invalidation.
162  */
163 static void dhcp_invalidate(struct connman_dhcp *dhcp, bool callback)
164 {
165         DBG("dhcp %p callback %u", dhcp, callback);
166
167         if (!dhcp)
168                 return;
169
170         __connman_6to4_remove(dhcp->ipconfig);
171
172         if (!apply_dhcp_invalidate_on_network(dhcp))
173                 return;
174
175         __connman_ipconfig_set_dhcp_address(dhcp->ipconfig,
176                                 __connman_ipconfig_get_local(dhcp->ipconfig));
177         DBG("last address %s",
178                         __connman_ipconfig_get_dhcp_address(dhcp->ipconfig));
179
180         __connman_ipconfig_address_remove(dhcp->ipconfig);
181
182         __connman_ipconfig_set_local(dhcp->ipconfig, NULL);
183         __connman_ipconfig_set_broadcast(dhcp->ipconfig, NULL);
184         __connman_ipconfig_set_gateway(dhcp->ipconfig, NULL);
185         __connman_ipconfig_set_prefixlen(dhcp->ipconfig, 0);
186
187         if (dhcp->callback && callback)
188                 dhcp->callback(dhcp->ipconfig, dhcp->network,
189                                                 false, dhcp->user_data);
190 }
191
192 static void dhcp_valid(struct connman_dhcp *dhcp)
193 {
194         if (dhcp->callback)
195                 dhcp->callback(dhcp->ipconfig, dhcp->network,
196                                                 true, dhcp->user_data);
197 }
198
199 static void dhcp_debug(const char *str, void *data)
200 {
201         connman_info("%s: %s", (const char *) data, str);
202 }
203
204 static void ipv4ll_lost_cb(GDHCPClient *dhcp_client, gpointer user_data);
205 static void ipv4ll_available_cb(GDHCPClient *ipv4ll_client, gpointer user_data);
206
207 static int ipv4ll_start_client(struct connman_dhcp *dhcp)
208 {
209         GDHCPClient *ipv4ll_client;
210         GDHCPClientError error;
211         const char *hostname;
212         int index;
213         int err;
214
215 #if defined TIZEN_EXT
216         DBG("dhcp %p", dhcp);
217 #endif
218
219         if (dhcp->ipv4ll_client)
220                 return -EALREADY;
221
222         index = __connman_ipconfig_get_index(dhcp->ipconfig);
223
224         ipv4ll_client = g_dhcp_client_new(G_DHCP_IPV4LL, index, &error);
225         if (error != G_DHCP_CLIENT_ERROR_NONE)
226                 return -EINVAL;
227
228 #if !defined TIZEN_EXT
229         if (getenv("CONNMAN_DHCP_DEBUG")) {
230 #endif
231                 dhcp->ipv4ll_debug_prefix = g_strdup_printf("IPv4LL index %d",
232                                                         index);
233                 g_dhcp_client_set_debug(ipv4ll_client, dhcp_debug,
234                                         dhcp->ipv4ll_debug_prefix);
235 #if !defined TIZEN_EXT
236         }
237 #endif
238
239         g_dhcp_client_set_id(ipv4ll_client);
240
241         if (dhcp->network) {
242                 hostname = connman_utsname_get_hostname();
243                 if (hostname)
244                         g_dhcp_client_set_send(ipv4ll_client,
245                                                 G_DHCP_HOST_NAME, hostname);
246         }
247
248         g_dhcp_client_register_event(ipv4ll_client,
249                         G_DHCP_CLIENT_EVENT_IPV4LL_LOST, ipv4ll_lost_cb, dhcp);
250
251         g_dhcp_client_register_event(ipv4ll_client,
252                         G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE,
253                                                 ipv4ll_available_cb, dhcp);
254
255         dhcp->ipv4ll_client = ipv4ll_client;
256
257         err = g_dhcp_client_start(dhcp->ipv4ll_client, NULL);
258         if (err < 0) {
259                 ipv4ll_stop_client(dhcp);
260                 return err;
261         }
262
263         dhcp->ipv4ll_running = true;
264         return 0;
265 }
266
267 static gboolean dhcp_retry_cb(gpointer user_data)
268 {
269         struct connman_dhcp *dhcp = user_data;
270
271         dhcp->timeout = 0;
272
273 #if defined TIZEN_EXT
274         DBG("dhcp %p", dhcp);
275         DBG("dhcp->timeout %d", dhcp->timeout);
276 #endif
277         g_dhcp_client_start(dhcp->dhcp_client,
278                         __connman_ipconfig_get_dhcp_address(dhcp->ipconfig));
279
280         return FALSE;
281 }
282
283 static void no_lease_cb(GDHCPClient *dhcp_client, gpointer user_data)
284 {
285         struct connman_dhcp *dhcp = user_data;
286         int err;
287
288         DBG("No lease available ipv4ll %d client %p", dhcp->ipv4ll_running,
289                 dhcp->ipv4ll_client);
290
291 #if defined TIZEN_EXT
292         if (dhcp->network &&
293                         connman_network_get_bool(dhcp->network, "WiFi.RoamingDHCP")) {
294                 int lease_time = 0;
295
296                 connman_network_set_bool(dhcp->network, "WiFi.RoamingDHCP", false);
297                 __connman_network_enable_ipconfig(dhcp->network, dhcp->ipconfig);
298                 __connman_network_notify_dhcp_changed("DHCP_FAIL", &lease_time);
299
300                 return;
301         }
302
303         if (connman_setting_get_bool("EnableAutoIp") == false) {
304                 DBG("link-local address autoconfiguration is disabled.");
305                 if (dhcp->network) {
306                         int lease_time = 0;
307                         DBG("[DHCP-C] auto ip is not used, set dhcp-fail error and disconnect");
308                         __connman_network_notify_dhcp_changed("DHCP_FAIL", &lease_time);
309                         connman_network_set_error(dhcp->network, CONNMAN_NETWORK_ERROR_DHCP_FAIL);
310                 }
311                 return;
312         }
313 #endif
314         if (dhcp->timeout > 0)
315                 g_source_remove(dhcp->timeout);
316
317         dhcp->timeout = g_timeout_add_seconds(RATE_LIMIT_INTERVAL,
318                                                 dhcp_retry_cb,
319                                                 dhcp);
320         if (dhcp->ipv4ll_running)
321                 return;
322
323         err = ipv4ll_start_client(dhcp);
324         if (err < 0)
325                 DBG("Cannot start ipv4ll client (%d/%s)", err, strerror(-err));
326
327         /* Only notify upper layer if we have a problem */
328         dhcp_invalidate(dhcp, !dhcp->ipv4ll_running);
329 }
330
331 static void lease_lost_cb(GDHCPClient *dhcp_client, gpointer user_data)
332 {
333         struct connman_dhcp *dhcp = user_data;
334
335         DBG("Lease lost");
336
337         /* Upper layer will decide what to do, e.g. nothing or retry. */
338         dhcp_invalidate(dhcp, true);
339 }
340
341 static void ipv4ll_lost_cb(GDHCPClient *dhcp_client, gpointer user_data)
342 {
343         struct connman_dhcp *dhcp = user_data;
344
345         DBG("Lease lost");
346
347         ipv4ll_stop_client(dhcp);
348
349         /*
350          * Since we lost our IPv4LL configuration we might as notify
351          * the upper layers.
352          */
353         dhcp_invalidate(dhcp, true);
354 }
355
356 static bool compare_string_arrays(char **array_a, char **array_b)
357 {
358         int i;
359
360         if (!array_a || !array_b)
361                 return false;
362
363         if (g_strv_length(array_a) != g_strv_length(array_b))
364                 return false;
365
366         for (i = 0; array_a[i] &&
367                              array_b[i]; i++) {
368                 if (g_strcmp0(array_a[i], array_b[i]) != 0)
369                         return false;
370         }
371
372         return true;
373 }
374
375 static bool apply_lease_available_on_network(GDHCPClient *dhcp_client,
376                                                 struct connman_dhcp *dhcp)
377 {
378         char **nameservers, **timeservers, *pac = NULL;
379         struct connman_service *service;
380         GList *list, *option = NULL;
381         int ns_entries;
382         int i;
383
384         if (!dhcp->network)
385                 return true;
386
387         service = connman_service_lookup_from_network(dhcp->network);
388         if (!service) {
389                 connman_error("Can not lookup service");
390                 return false;
391         }
392
393         option = g_dhcp_client_get_option(dhcp_client, G_DHCP_MTU);
394         if (option && option->data) {
395                 int mtu, index, err;
396
397                 mtu = atoi(option->data);
398
399                 if (mtu >= IPV6_MIN_MTU && mtu <= ETH_DATA_LEN) {
400                         index = __connman_ipconfig_get_index(dhcp->ipconfig);
401                         err = connman_inet_set_mtu(index, mtu);
402
403                         DBG("MTU %d index %d err %d", mtu, index, err);
404                 }
405         }
406
407         option = g_dhcp_client_get_option(dhcp_client, 252);
408         if (option)
409                 pac = g_strdup(option->data);
410
411         option = g_dhcp_client_get_option(dhcp_client, G_DHCP_DNS_SERVER);
412         ns_entries = g_list_length(option);
413         nameservers = g_try_new0(char *, ns_entries + 1);
414         if (nameservers) {
415                 for (i = 0, list = option;list; list = list->next, i++)
416                         nameservers[i] = g_strdup(list->data);
417                 nameservers[ns_entries] = NULL;
418         }
419
420         option = g_dhcp_client_get_option(dhcp_client, G_DHCP_DOMAIN_NAME);
421         if (option)
422                 __connman_service_set_domainname(service, option->data);
423
424         option = g_dhcp_client_get_option(dhcp_client, G_DHCP_HOST_NAME);
425         if (option)
426                 __connman_service_set_hostname(service, option->data);
427
428         option = g_dhcp_client_get_option(dhcp_client, G_DHCP_NTP_SERVER);
429         ns_entries = g_list_length(option);
430         timeservers = g_try_new0(char *, ns_entries + 1);
431         if (timeservers) {
432                 for (i = 0, list = option; list; list = list->next, i++)
433                         timeservers[i] = g_strdup(list->data);
434                 timeservers[ns_entries] = NULL;
435         }
436
437         if (!compare_string_arrays(nameservers, dhcp->nameservers)) {
438                 if (dhcp->nameservers) {
439 #if defined TIZEN_EXT
440                         for (i = 0; dhcp->nameservers[i] != NULL; i++) {
441                                 __connman_service_nameserver_remove(service,
442                                                 dhcp->nameservers[i], false,
443                                                 CONNMAN_IPCONFIG_TYPE_IPV4);
444                         }
445 #else
446                         for (i = 0; dhcp->nameservers[i]; i++) {
447                                 __connman_service_nameserver_remove(service,
448                                                 dhcp->nameservers[i], false);
449                         }
450 #endif
451                         g_strfreev(dhcp->nameservers);
452                 }
453
454                 dhcp->nameservers = nameservers;
455
456                 for (i = 0; dhcp->nameservers && dhcp->nameservers[i]; i++) {
457 #if defined TIZEN_EXT
458                         __connman_service_nameserver_append(service,
459                                                 dhcp->nameservers[i], false,
460                                                 CONNMAN_IPCONFIG_TYPE_IPV4);
461 #else
462                         __connman_service_nameserver_append(service,
463                                                 dhcp->nameservers[i], false);
464 #endif
465                 }
466         } else {
467                 g_strfreev(nameservers);
468         }
469
470         if (!compare_string_arrays(timeservers, dhcp->timeservers)) {
471                 if (dhcp->timeservers) {
472                         for (i = 0; dhcp->timeservers[i]; i++) {
473                                 __connman_service_timeserver_remove(service,
474                                                         dhcp->timeservers[i]);
475                         }
476                         g_strfreev(dhcp->timeservers);
477                 }
478
479                 dhcp->timeservers = timeservers;
480
481                 for (i = 0; dhcp->timeservers && dhcp->timeservers[i]; i++) {
482                         __connman_service_timeserver_append(service,
483                                                         dhcp->timeservers[i]);
484                 }
485         } else {
486                 g_strfreev(timeservers);
487         }
488
489         if (g_strcmp0(pac, dhcp->pac) != 0) {
490                 g_free(dhcp->pac);
491                 dhcp->pac = pac;
492
493                 __connman_ipconfig_set_proxy_autoconfig(dhcp->ipconfig,
494                                                                 dhcp->pac);
495         }
496
497         if (connman_setting_get_bool("Enable6to4"))
498                 __connman_6to4_probe(service);
499
500         return true;
501 }
502
503 static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
504 {
505         struct connman_dhcp *dhcp = user_data;
506         GList *option = NULL;
507         enum connman_ipconfig_method old_method;
508         char *address, *netmask = NULL, *gateway = NULL;
509         const char *c_address, *c_gateway;
510         unsigned char prefixlen, c_prefixlen;
511         bool ip_change = false;
512
513         DBG("Lease available");
514
515         if (dhcp->ipv4ll_client) {
516                 ipv4ll_stop_client(dhcp);
517                 dhcp_invalidate(dhcp, false);
518         }
519
520         c_address = __connman_ipconfig_get_local(dhcp->ipconfig);
521         c_gateway = __connman_ipconfig_get_gateway(dhcp->ipconfig);
522         c_prefixlen = __connman_ipconfig_get_prefixlen(dhcp->ipconfig);
523
524         address = g_dhcp_client_get_address(dhcp_client);
525
526         __connman_ipconfig_set_dhcp_address(dhcp->ipconfig, address);
527         DBG("last address %s", address);
528
529 #if defined TIZEN_EXT
530         int dhcp_lease_duration = g_dhcp_client_get_dhcp_lease_duration(dhcp_client);
531 #endif
532
533         option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET);
534         if (option)
535                 netmask = g_strdup(option->data);
536
537         option = g_dhcp_client_get_option(dhcp_client, G_DHCP_ROUTER);
538         if (option)
539                 gateway = g_strdup(option->data);
540
541         prefixlen = connman_ipaddress_calc_netmask_len(netmask);
542         if (prefixlen == 255)
543                 connman_warn("netmask: %s is invalid", netmask);
544
545         DBG("c_address %s", c_address);
546
547         if (g_strcmp0(address, c_address)) {
548                 ip_change = true;
549                 if (c_address) {
550                         /* Remove old ip address */
551                         __connman_ipconfig_address_remove(dhcp->ipconfig);
552                 }
553         }
554         if (g_strcmp0(gateway, c_gateway)) {
555                 ip_change = true;
556                 if (c_gateway) {
557                         /* Remove gateway ip address */
558                         __connman_ipconfig_gateway_remove(dhcp->ipconfig);
559                 }
560         } else if (prefixlen != c_prefixlen)
561                 ip_change = true;
562
563         old_method = __connman_ipconfig_get_method(dhcp->ipconfig);
564         __connman_ipconfig_set_method(dhcp->ipconfig,
565                                                 CONNMAN_IPCONFIG_METHOD_DHCP);
566
567 #if defined TIZEN_EXT
568         __connman_ipconfig_set_dhcp_lease_duration(dhcp->ipconfig, dhcp_lease_duration);
569         __connman_network_notify_dhcp_changed(DHCP_SUCCESS, &dhcp_lease_duration);
570 #endif
571
572         /*
573          * Notify IPv4.Configuration's method moved back to DHCP.
574          *
575          * This is the case ConnMan initially set an address by using
576          * IPv4LL because DHCP failed but now we got an address from DHCP.
577          */
578         if (old_method == CONNMAN_IPCONFIG_METHOD_AUTO) {
579                 struct connman_service *service =
580                         connman_service_lookup_from_network(dhcp->network);
581
582                 if (service)
583                         __connman_service_notify_ipv4_configuration(service);
584         }
585
586 #if defined TIZEN_EXT
587         if (connman_network_get_bool(dhcp->network, "WiFi.RoamingDHCP")) {
588
589                 if (ip_change)
590                         connman_service_notify_reconnection(
591                                 connman_service_lookup_from_network(dhcp->network));
592
593                 connman_network_set_bool(dhcp->network, "WiFi.RoamingDHCP", false);
594         }
595 #endif
596
597         if (ip_change) {
598                 __connman_ipconfig_set_local(dhcp->ipconfig, address);
599                 __connman_ipconfig_set_prefixlen(dhcp->ipconfig, prefixlen);
600                 __connman_ipconfig_set_gateway(dhcp->ipconfig, gateway);
601         }
602
603         if (!apply_lease_available_on_network(dhcp_client, dhcp))
604                 goto done;
605
606         if (ip_change)
607                 dhcp_valid(dhcp);
608
609 done:
610         g_free(address);
611         g_free(netmask);
612         g_free(gateway);
613 }
614
615 static void ipv4ll_available_cb(GDHCPClient *ipv4ll_client, gpointer user_data)
616 {
617         struct connman_dhcp *dhcp = user_data;
618         enum connman_ipconfig_method old_method;
619         char *address, *netmask;
620         unsigned char prefixlen;
621
622         DBG("IPV4LL available");
623
624         address = g_dhcp_client_get_address(ipv4ll_client);
625         netmask = g_dhcp_client_get_netmask(ipv4ll_client);
626
627         prefixlen = connman_ipaddress_calc_netmask_len(netmask);
628
629         old_method = __connman_ipconfig_get_method(dhcp->ipconfig);
630         __connman_ipconfig_set_method(dhcp->ipconfig,
631                                                 CONNMAN_IPCONFIG_METHOD_AUTO);
632
633         /*
634          * Notify IPv4.Configuration's method is AUTO now.
635          *
636          * This is the case DHCP failed thus ConnMan used IPv4LL to get an
637          * address. Set IPv4.Configuration method to AUTO allows user to
638          * ask for a DHCP address by setting the method again to DHCP.
639          */
640         if (old_method == CONNMAN_IPCONFIG_METHOD_DHCP) {
641                 struct connman_service *service =
642                         connman_service_lookup_from_network(dhcp->network);
643
644                 if (service)
645                         __connman_service_notify_ipv4_configuration(service);
646         }
647
648         __connman_ipconfig_set_local(dhcp->ipconfig, address);
649         __connman_ipconfig_set_prefixlen(dhcp->ipconfig, prefixlen);
650         __connman_ipconfig_set_gateway(dhcp->ipconfig, NULL);
651
652         dhcp_valid(dhcp);
653
654         g_free(address);
655         g_free(netmask);
656 }
657
658 static int dhcp_initialize(struct connman_dhcp *dhcp)
659 {
660         GDHCPClient *dhcp_client;
661         GDHCPClientError error;
662         int index;
663         const char *vendor_class_id;
664
665         DBG("dhcp %p", dhcp);
666
667         index = __connman_ipconfig_get_index(dhcp->ipconfig);
668
669         dhcp_client = g_dhcp_client_new(G_DHCP_IPV4, index, &error);
670         if (error != G_DHCP_CLIENT_ERROR_NONE)
671 #if defined TIZEN_EXT
672         {
673                 DBG("failed g_dhcp_client_new(%d), index(%d)", error, index);
674 #endif
675                 return -EINVAL;
676 #if defined TIZEN_EXT
677         }
678 #endif
679
680 #if !defined TIZEN_EXT
681         if (getenv("CONNMAN_DHCP_DEBUG")) {
682 #endif
683                 dhcp->dhcp_debug_prefix = g_strdup_printf("DHCP index %d",
684                                                         index);
685                 g_dhcp_client_set_debug(dhcp_client, dhcp_debug,
686                                         dhcp->dhcp_debug_prefix);
687 #if !defined TIZEN_EXT
688         }
689 #endif
690
691         g_dhcp_client_set_id(dhcp_client);
692
693         if (dhcp->network) {
694                 struct connman_service *service;
695                 const char *hostname;
696
697                 service = connman_service_lookup_from_network(dhcp->network);
698
699                 hostname = __connman_service_get_hostname(service);
700                 if (!hostname)
701                         hostname = connman_utsname_get_hostname();
702
703                 if (hostname)
704                         g_dhcp_client_set_send(dhcp_client,
705                                                 G_DHCP_HOST_NAME, hostname);
706
707                 g_dhcp_client_set_request(dhcp_client, G_DHCP_HOST_NAME);
708                 g_dhcp_client_set_request(dhcp_client, G_DHCP_DNS_SERVER);
709                 g_dhcp_client_set_request(dhcp_client, G_DHCP_DOMAIN_NAME);
710                 g_dhcp_client_set_request(dhcp_client, G_DHCP_NTP_SERVER);
711                 g_dhcp_client_set_request(dhcp_client, 252);
712                 g_dhcp_client_set_request(dhcp_client, G_DHCP_MTU);
713         }
714
715         g_dhcp_client_set_request(dhcp_client, G_DHCP_ROUTER);
716         g_dhcp_client_set_request(dhcp_client, G_DHCP_SUBNET);
717
718         vendor_class_id = connman_setting_get_string("VendorClassID");
719         if (vendor_class_id)
720                 g_dhcp_client_set_send(dhcp_client, G_DHCP_VENDOR_CLASS_ID,
721                                         vendor_class_id);
722
723         g_dhcp_client_register_event(dhcp_client,
724                         G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE,
725                                                 lease_available_cb, dhcp);
726
727         g_dhcp_client_register_event(dhcp_client,
728                         G_DHCP_CLIENT_EVENT_LEASE_LOST, lease_lost_cb, dhcp);
729
730         g_dhcp_client_register_event(dhcp_client,
731                         G_DHCP_CLIENT_EVENT_NO_LEASE, no_lease_cb, dhcp);
732
733         dhcp->dhcp_client = dhcp_client;
734
735         return 0;
736 }
737
738 static int dhcp_release(struct connman_dhcp *dhcp)
739 {
740         DBG("dhcp %p", dhcp);
741
742         if (dhcp->timeout > 0) {
743                 g_source_remove(dhcp->timeout);
744                 dhcp->timeout = 0;
745         }
746
747         if (dhcp->dhcp_client) {
748                 g_dhcp_client_stop(dhcp->dhcp_client);
749                 g_dhcp_client_unref(dhcp->dhcp_client);
750         }
751
752         dhcp->dhcp_client = NULL;
753
754         g_free(dhcp->dhcp_debug_prefix);
755         dhcp->dhcp_debug_prefix = NULL;
756
757         ipv4ll_stop_client(dhcp);
758
759         return 0;
760 }
761
762 char *__connman_dhcp_get_server_address(struct connman_ipconfig *ipconfig)
763 {
764         struct connman_dhcp *dhcp;
765
766         dhcp = g_hash_table_lookup(ipconfig_table, ipconfig);
767         if (!dhcp)
768                 return NULL;
769
770         return g_dhcp_client_get_server_address(dhcp->dhcp_client);
771 }
772
773 #if defined TIZEN_EXT_WIFI_MESH
774 int __connman_mesh_dhcp_start(struct connman_ipconfig *ipconfig,
775                         dhcp_cb callback, gpointer user_data)
776 {
777         struct connman_dhcp *dhcp;
778         int err;
779
780         DBG("");
781
782         dhcp = g_hash_table_lookup(ipconfig_table, ipconfig);
783         if (!dhcp) {
784
785                 dhcp = g_try_new0(struct connman_dhcp, 1);
786                 if (!dhcp)
787                         return -ENOMEM;
788
789                 dhcp->ipconfig = ipconfig;
790                 __connman_ipconfig_ref(ipconfig);
791
792                 err = dhcp_initialize(dhcp);
793
794                 if (err < 0) {
795                         g_free(dhcp);
796                         return err;
797                 }
798
799                 g_hash_table_insert(ipconfig_table, ipconfig, dhcp);
800         }
801
802         dhcp->callback = callback;
803         dhcp->user_data = user_data;
804         return g_dhcp_client_start(dhcp->dhcp_client, NULL);
805 }
806 #endif
807
808 int __connman_dhcp_start(struct connman_ipconfig *ipconfig,
809                         struct connman_network *network, dhcp_cb callback,
810                         gpointer user_data)
811 {
812 #if !defined TIZEN_EXT
813         const char *last_addr = NULL;
814 #endif
815         struct connman_dhcp *dhcp;
816         int err;
817
818         DBG("");
819
820         if (network) {
821                 struct connman_service *service;
822
823                 service = connman_service_lookup_from_network(network);
824                 if (!service)
825                         return -EINVAL;
826         }
827
828 #if !defined TIZEN_EXT
829         last_addr = __connman_ipconfig_get_dhcp_address(ipconfig);
830 #endif
831
832         dhcp = g_hash_table_lookup(ipconfig_table, ipconfig);
833         if (!dhcp) {
834
835                 dhcp = g_try_new0(struct connman_dhcp, 1);
836                 if (!dhcp)
837                         return -ENOMEM;
838
839                 dhcp->ipconfig = ipconfig;
840                 __connman_ipconfig_ref(ipconfig);
841
842                 if (network) {
843                         dhcp->network = network;
844                         connman_network_ref(network);
845                 }
846
847                 err = dhcp_initialize(dhcp);
848
849                 if (err < 0) {
850                         if (network)
851                                 connman_network_unref(network);
852                         g_free(dhcp);
853                         return err;
854                 }
855
856                 g_hash_table_insert(ipconfig_table, ipconfig, dhcp);
857         }
858
859         dhcp->callback = callback;
860         dhcp->user_data = user_data;
861
862 #if defined TIZEN_EXT
863         if (network && connman_network_get_bool(network, "WiFi.RoamingDHCP")) {
864                 const char *last_addr = __connman_ipconfig_get_dhcp_address(ipconfig);
865
866                 DBG("Start DHCP with last address request");
867                 return g_dhcp_client_start(dhcp->dhcp_client, last_addr);
868         } else {
869                 DBG("Start DHCP with DHCPDISCOVER request");
870                 return g_dhcp_client_start(dhcp->dhcp_client, NULL);
871         }
872 #else
873         return g_dhcp_client_start(dhcp->dhcp_client, last_addr);
874 #endif
875 }
876
877 void __connman_dhcp_stop(struct connman_ipconfig *ipconfig)
878 {
879         struct connman_dhcp *dhcp;
880
881         DBG("ipconfig_table %p ipconfig %p", ipconfig_table, ipconfig);
882
883         if (!ipconfig_table)
884                 return;
885
886         dhcp = g_hash_table_lookup(ipconfig_table, ipconfig);
887         if (dhcp) {
888                 g_hash_table_remove(ipconfig_table, ipconfig);
889                 __connman_ipconfig_unref(ipconfig);
890                 if (dhcp->network)
891                         connman_network_unref(dhcp->network);
892                 dhcp_release(dhcp);
893                 dhcp_invalidate(dhcp, false);
894                 dhcp_free(dhcp);
895         }
896 }
897
898 void __connman_dhcp_decline(struct connman_ipconfig *ipconfig)
899 {
900         struct connman_dhcp *dhcp;
901         const char *address;
902         struct in_addr addr;
903
904         DBG("ipconfig_table %p ipconfig %p", ipconfig_table, ipconfig);
905
906         if (!ipconfig_table)
907                 return;
908
909         dhcp = g_hash_table_lookup(ipconfig_table, ipconfig);
910         if (dhcp) {
911                 address = __connman_ipconfig_get_local(ipconfig);
912                 if (!address)
913                         return;
914
915                 if (inet_pton(AF_INET, address, &addr) != 1)
916                         connman_error("Could not convert address %s", address);
917
918                 g_dhcp_client_decline(dhcp->dhcp_client, htonl(addr.s_addr));
919         }
920 }
921
922 int __connman_dhcp_init(void)
923 {
924         DBG("");
925
926         ipconfig_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
927                                                                 NULL, NULL);
928
929         return 0;
930 }
931
932 void __connman_dhcp_cleanup(void)
933 {
934         DBG("");
935
936         g_hash_table_destroy(ipconfig_table);
937         ipconfig_table = NULL;
938 }