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