Add support to get PMF information
[platform/upstream/connman.git] / src / dhcpv6.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2012-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/if.h>
30
31 #include <connman/ipconfig.h>
32 #include <connman/storage.h>
33
34 #include <gdhcp/gdhcp.h>
35
36 #include <glib.h>
37
38 #include "connman.h"
39
40 /* Transmission params in msec, RFC 3315 chapter 5.5 */
41 #define INF_MAX_DELAY   (1 * 1000)
42 #define INF_TIMEOUT     (1 * 1000)
43 #define INF_MAX_RT      (120 * 1000)
44 #define SOL_MAX_DELAY   (1 * 1000)
45 #define SOL_TIMEOUT     (1 * 1000)
46 #define SOL_MAX_RT      (120 * 1000)
47 #define REQ_TIMEOUT     (1 * 1000)
48 #define REQ_MAX_RT      (30 * 1000)
49 #define REQ_MAX_RC      10
50 #define REN_TIMEOUT     (10 * 1000)
51 #define REN_MAX_RT      (600 * 1000)
52 #define REB_TIMEOUT     (10 * 1000)
53 #define REB_MAX_RT      (600 * 1000)
54 #define CNF_MAX_DELAY   (1 * 1000)
55 #define CNF_TIMEOUT     (1 * 1000)
56 #define CNF_MAX_RT      (4 * 1000)
57 #define CNF_MAX_RD      (10 * 1000)
58 #define DEC_TIMEOUT     (1 * 1000)
59 #define DEC_MAX_RC      5
60
61 enum request_type {
62         REQ_REQUEST = 1,
63         REQ_REBIND = 2,
64         REQ_RENEW = 3,
65 };
66
67 struct connman_dhcpv6 {
68         struct connman_network *network;
69         dhcpv6_cb callback;
70
71         char **nameservers;
72         char **timeservers;
73
74         GDHCPClient *dhcp_client;
75
76         guint timeout;          /* operation timeout in msec */
77         guint MRD;              /* max operation timeout in msec */
78         guint RT;               /* in msec */
79         bool use_ta;            /* set to TRUE if IPv6 privacy is enabled */
80         GSList *prefixes;       /* network prefixes from radvd or dhcpv6 pd */
81         int request_count;      /* how many times REQUEST have been sent */
82         bool stateless;         /* TRUE if stateless DHCPv6 is used */
83         bool started;           /* TRUE if we have DHCPv6 started */
84 };
85
86 static GHashTable *network_table;
87 static GHashTable *network_pd_table;
88
89 static int dhcpv6_request(struct connman_dhcpv6 *dhcp, bool add_addresses);
90 static int dhcpv6_pd_request(struct connman_dhcpv6 *dhcp);
91 static gboolean start_solicitation(gpointer user_data);
92 static int dhcpv6_renew(struct connman_dhcpv6 *dhcp);
93 static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp);
94
95 static void clear_timer(struct connman_dhcpv6 *dhcp)
96 {
97         if (dhcp->timeout > 0) {
98                 g_source_remove(dhcp->timeout);
99                 dhcp->timeout = 0;
100         }
101
102         if (dhcp->MRD > 0) {
103                 g_source_remove(dhcp->MRD);
104                 dhcp->MRD = 0;
105         }
106 }
107
108 static guint compute_random(guint val)
109 {
110         uint64_t rand;
111
112         __connman_util_get_random(&rand);
113
114         return val - val / 10 +
115                 ((guint) rand % (2 * 1000)) * val / 10 / 1000;
116 }
117
118 /* Calculate a random delay, RFC 3315 chapter 14 */
119 /* RT and MRT are milliseconds */
120 static guint calc_delay(guint RT, guint MRT)
121 {
122         if (MRT && (RT > MRT / 2))
123                 RT = compute_random(MRT);
124         else
125                 RT += compute_random(RT);
126
127         return RT;
128 }
129
130 static guint initial_rt(guint timeout)
131 {
132         return compute_random(timeout);
133 }
134
135 static void free_prefix(gpointer data)
136 {
137         g_free(data);
138 }
139
140 static void dhcpv6_free(struct connman_dhcpv6 *dhcp)
141 {
142         g_strfreev(dhcp->nameservers);
143         g_strfreev(dhcp->timeservers);
144
145         dhcp->nameservers = NULL;
146         dhcp->timeservers = NULL;
147         dhcp->started = false;
148
149         g_slist_free_full(dhcp->prefixes, free_prefix);
150 }
151
152 static bool compare_string_arrays(char **array_a, char **array_b)
153 {
154         int i;
155
156         if (!array_a || !array_b)
157                 return false;
158
159         if (g_strv_length(array_a) != g_strv_length(array_b))
160                 return false;
161
162         for (i = 0; array_a[i] && array_b[i]; i++)
163                 if (g_strcmp0(array_a[i], array_b[i]) != 0)
164                         return false;
165
166         return true;
167 }
168
169 static void dhcpv6_debug(const char *str, void *data)
170 {
171         connman_info("%s: %s\n", (const char *) data, str);
172 }
173
174 static gchar *convert_to_hex(unsigned char *buf, int len)
175 {
176         gchar *ret = g_try_malloc(len * 2 + 1);
177         int i;
178
179         for (i = 0; ret && i < len; i++)
180                 g_snprintf(ret + i * 2, 3, "%02x", buf[i]);
181
182         return ret;
183 }
184
185 /*
186  * DUID should not change over time so save it to file.
187  * See RFC 3315 chapter 9 for details.
188  */
189 static int set_duid(struct connman_service *service,
190                         struct connman_network *network,
191                         GDHCPClient *dhcp_client, int index)
192 {
193         GKeyFile *keyfile;
194         const char *ident;
195         char *hex_duid;
196         unsigned char *duid;
197         int duid_len;
198
199         ident = connman_service_get_identifier(service);
200 #if defined TIZEN_EXT
201         if(ident != NULL)
202                 DBG("ident : %s", ident);
203 #endif
204
205         keyfile = connman_storage_load_service(ident);
206
207 #if defined TIZEN_EXT
208         if (!keyfile) {
209                 keyfile = g_key_file_new();
210                 if (!keyfile)
211                         return -EIO;
212         }
213 #else
214         if (!keyfile)
215                 return -EINVAL;
216 #endif
217
218         hex_duid = g_key_file_get_string(keyfile, ident, "IPv6.DHCP.DUID",
219                                         NULL);
220         if (hex_duid) {
221                 unsigned int i, j = 0, hex;
222                 size_t hex_duid_len = strlen(hex_duid);
223
224                 duid = g_try_malloc0(hex_duid_len / 2);
225                 if (!duid) {
226                         g_key_file_free(keyfile);
227                         g_free(hex_duid);
228                         return -ENOMEM;
229                 }
230
231                 for (i = 0; i < hex_duid_len; i += 2) {
232                         sscanf(hex_duid + i, "%02x", &hex);
233                         duid[j++] = hex;
234                 }
235
236                 duid_len = hex_duid_len / 2;
237         } else {
238                 int ret;
239                 int type = __connman_ipconfig_get_type_from_index(index);
240
241                 ret = g_dhcpv6_create_duid(G_DHCPV6_DUID_LLT, index, type,
242                                         &duid, &duid_len);
243                 if (ret < 0) {
244                         g_key_file_free(keyfile);
245                         return ret;
246                 }
247
248                 hex_duid = convert_to_hex(duid, duid_len);
249                 if (!hex_duid) {
250                         g_free(duid);
251                         g_key_file_free(keyfile);
252                         return -ENOMEM;
253                 }
254
255                 g_key_file_set_string(keyfile, ident, "IPv6.DHCP.DUID",
256                                 hex_duid);
257
258                 __connman_storage_save_service(keyfile, ident);
259         }
260         g_free(hex_duid);
261
262         g_key_file_free(keyfile);
263
264         g_dhcpv6_client_set_duid(dhcp_client, duid, duid_len);
265
266         return 0;
267 }
268
269 static void clear_callbacks(GDHCPClient *dhcp_client)
270 {
271         g_dhcp_client_register_event(dhcp_client,
272                                 G_DHCP_CLIENT_EVENT_SOLICITATION,
273                                 NULL, NULL);
274
275         g_dhcp_client_register_event(dhcp_client,
276                                 G_DHCP_CLIENT_EVENT_ADVERTISE,
277                                 NULL, NULL);
278
279         g_dhcp_client_register_event(dhcp_client,
280                                 G_DHCP_CLIENT_EVENT_REQUEST,
281                                 NULL, NULL);
282
283         g_dhcp_client_register_event(dhcp_client,
284                                 G_DHCP_CLIENT_EVENT_CONFIRM,
285                                 NULL, NULL);
286
287         g_dhcp_client_register_event(dhcp_client,
288                                 G_DHCP_CLIENT_EVENT_RENEW,
289                                 NULL, NULL);
290
291         g_dhcp_client_register_event(dhcp_client,
292                                 G_DHCP_CLIENT_EVENT_REBIND,
293                                 NULL, NULL);
294
295         g_dhcp_client_register_event(dhcp_client,
296                                 G_DHCP_CLIENT_EVENT_RELEASE,
297                                 NULL, NULL);
298
299         g_dhcp_client_register_event(dhcp_client,
300                                 G_DHCP_CLIENT_EVENT_DECLINE,
301                                 NULL, NULL);
302
303         g_dhcp_client_register_event(dhcp_client,
304                                 G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
305                                 NULL, NULL);
306 }
307
308 static void info_req_cb(GDHCPClient *dhcp_client, gpointer user_data)
309 {
310         struct connman_dhcpv6 *dhcp = user_data;
311         struct connman_service *service;
312         int entries, i;
313         GList *option, *list;
314         char **nameservers, **timeservers;
315
316         DBG("dhcpv6 information-request %p", dhcp);
317
318         service = connman_service_lookup_from_network(dhcp->network);
319         if (!service) {
320                 connman_error("Can not lookup service");
321                 return;
322         }
323
324         g_dhcpv6_client_clear_retransmit(dhcp_client);
325
326         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
327         entries = g_list_length(option);
328
329         nameservers = g_try_new0(char *, entries + 1);
330         if (nameservers) {
331                 for (i = 0, list = option; list; list = list->next, i++)
332                         nameservers[i] = g_strdup(list->data);
333         }
334
335         if (!compare_string_arrays(nameservers, dhcp->nameservers)) {
336                 if (dhcp->nameservers) {
337                         for (i = 0; dhcp->nameservers[i]; i++)
338 #if defined TIZEN_EXT
339                         {
340                                 __connman_service_nameserver_remove(service,
341                                                 dhcp->nameservers[i], false,
342                                                 CONNMAN_IPCONFIG_TYPE_IPV6);
343 #else
344                                 __connman_service_nameserver_remove(service,
345                                                         dhcp->nameservers[i],
346                                                         false);
347 #endif
348 #if defined TIZEN_EXT
349                         }
350 #endif
351                         g_strfreev(dhcp->nameservers);
352                 }
353
354                 dhcp->nameservers = nameservers;
355
356                 for (i = 0; dhcp->nameservers &&
357                                         dhcp->nameservers[i]; i++)
358 #if defined TIZEN_EXT
359                 {
360                         __connman_service_nameserver_append(service,
361                                                 dhcp->nameservers[i], false,
362                                                 CONNMAN_IPCONFIG_TYPE_IPV6);
363 #else
364                         __connman_service_nameserver_append(service,
365                                                 dhcp->nameservers[i],
366                                                 false);
367 #endif
368 #if defined TIZEN_EXT
369                 }
370 #endif
371         } else
372                 g_strfreev(nameservers);
373
374
375         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
376         entries = g_list_length(option);
377
378         timeservers = g_try_new0(char *, entries + 1);
379         if (timeservers) {
380                 for (i = 0, list = option; list; list = list->next, i++)
381                         timeservers[i] = g_strdup(list->data);
382         }
383
384         if (!compare_string_arrays(timeservers, dhcp->timeservers)) {
385                 if (dhcp->timeservers) {
386                         for (i = 0; dhcp->timeservers[i]; i++)
387                                 __connman_service_timeserver_remove(service,
388                                                         dhcp->timeservers[i]);
389                         g_strfreev(dhcp->timeservers);
390                 }
391
392                 dhcp->timeservers = timeservers;
393
394                 for (i = 0; dhcp->timeservers &&
395                                         dhcp->timeservers[i]; i++)
396                         __connman_service_timeserver_append(service,
397                                                         dhcp->timeservers[i]);
398         } else
399                 g_strfreev(timeservers);
400
401
402         if (dhcp->callback) {
403                 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
404                 dhcp->callback(dhcp->network, status == 0 ?
405                                                 CONNMAN_DHCPV6_STATUS_SUCCEED :
406                                                 CONNMAN_DHCPV6_STATUS_FAIL,
407                                 NULL);
408         }
409 }
410
411 static int dhcpv6_info_request(struct connman_dhcpv6 *dhcp)
412 {
413         struct connman_service *service;
414         GDHCPClient *dhcp_client;
415         GDHCPClientError error;
416         int index, ret;
417
418         DBG("dhcp %p", dhcp);
419
420         index = connman_network_get_index(dhcp->network);
421
422         dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
423         if (error != G_DHCP_CLIENT_ERROR_NONE) {
424                 clear_timer(dhcp);
425                 return -EINVAL;
426         }
427
428 #if !defined TIZEN_EXT
429         if (getenv("CONNMAN_DHCPV6_DEBUG"))
430 #endif
431                 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
432
433         service = connman_service_lookup_from_network(dhcp->network);
434         if (!service) {
435                 clear_timer(dhcp);
436                 g_dhcp_client_unref(dhcp_client);
437                 return -EINVAL;
438         }
439
440         ret = set_duid(service, dhcp->network, dhcp_client, index);
441         if (ret < 0) {
442                 clear_timer(dhcp);
443                 g_dhcp_client_unref(dhcp_client);
444                 return ret;
445         }
446
447         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
448         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
449         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
450         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
451
452         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
453                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
454
455         g_dhcp_client_register_event(dhcp_client,
456                         G_DHCP_CLIENT_EVENT_INFORMATION_REQ, info_req_cb, dhcp);
457
458         dhcp->dhcp_client = dhcp_client;
459
460         return g_dhcp_client_start(dhcp_client, NULL);
461 }
462
463 static int check_ipv6_addr_prefix(GSList *prefixes, char *address)
464 {
465         struct in6_addr addr_prefix, addr;
466         GSList *list;
467         int ret = 128, len;
468
469         for (list = prefixes; list; list = list->next) {
470                 char *prefix = list->data;
471                 const char *slash = g_strrstr(prefix, "/");
472                 const unsigned char bits[] = { 0x00, 0xFE, 0xFC, 0xF8,
473                                                 0xF0, 0xE0, 0xC0, 0x80 };
474                 int left, count, i, plen;
475
476                 if (!slash)
477                         continue;
478
479                 len = strtol(slash + 1, NULL, 10);
480                 if (len < 3 || len > 128)
481                         break;
482
483                 plen = 128 - len;
484
485                 count = plen / 8;
486                 left = plen % 8;
487                 i = 16 - count;
488
489                 prefix = g_strndup(prefix, slash - prefix);
490                 inet_pton(AF_INET6, prefix, &addr_prefix);
491                 inet_pton(AF_INET6, address, &addr);
492
493                 memset(&addr_prefix.s6_addr[i], 0, count);
494                 memset(&addr.s6_addr[i], 0, count);
495
496                 if (left) {
497                         addr_prefix.s6_addr[i - 1] &= bits[left];
498                         addr.s6_addr[i - 1] &= bits[left];
499                 }
500
501                 g_free(prefix);
502
503                 if (memcmp(&addr_prefix, &addr, 16) == 0) {
504                         ret = len;
505                         break;
506                 }
507         }
508
509         return ret;
510 }
511
512 static int set_other_addresses(GDHCPClient *dhcp_client,
513                                                 struct connman_dhcpv6 *dhcp)
514 {
515         struct connman_service *service;
516         int entries, i;
517         GList *option, *list;
518         char **nameservers, **timeservers;
519
520         service = connman_service_lookup_from_network(dhcp->network);
521         if (!service) {
522                 connman_error("Can not lookup service");
523                 return -EINVAL;
524         }
525
526         /*
527          * Check domains before nameservers so that the nameserver append
528          * function will update domain list in service.c
529          */
530         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DOMAIN_LIST);
531         entries = g_list_length(option);
532         if (entries > 0) {
533                 char **domains = g_try_new0(char *, entries + 1);
534                 if (domains) {
535                         for (i = 0, list = option; list;
536                                                 list = list->next, i++)
537                                 domains[i] = g_strdup(list->data);
538                         __connman_service_set_search_domains(service, domains);
539                         g_strfreev(domains);
540                 }
541         }
542
543         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
544         entries = g_list_length(option);
545
546         nameservers = g_try_new0(char *, entries + 1);
547         if (nameservers) {
548                 for (i = 0, list = option; list; list = list->next, i++)
549                         nameservers[i] = g_strdup(list->data);
550         }
551
552         if (!compare_string_arrays(nameservers, dhcp->nameservers)) {
553                 if (dhcp->nameservers) {
554                         for (i = 0; dhcp->nameservers[i]; i++)
555 #if defined TIZEN_EXT
556                         {
557                                 __connman_service_nameserver_remove(service,
558                                                 dhcp->nameservers[i],
559                                                 false, CONNMAN_IPCONFIG_TYPE_IPV6);
560 #else
561                                 __connman_service_nameserver_remove(service,
562                                                         dhcp->nameservers[i],
563                                                         false);
564 #endif
565 #if defined TIZEN_EXT
566                         }
567 #endif
568                         g_strfreev(dhcp->nameservers);
569                 }
570
571                 dhcp->nameservers = nameservers;
572
573                 for (i = 0; dhcp->nameservers &&
574                                         dhcp->nameservers[i]; i++)
575 #if defined TIZEN_EXT
576                 {
577                         __connman_service_nameserver_append(service,
578                                         dhcp->nameservers[i],
579                                         false, CONNMAN_IPCONFIG_TYPE_IPV6);
580 #else
581                         __connman_service_nameserver_append(service,
582                                                         dhcp->nameservers[i],
583                                                         false);
584 #endif
585 #if defined TIZEN_EXT
586                 }
587 #endif
588         } else
589                 g_strfreev(nameservers);
590
591
592         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
593         entries = g_list_length(option);
594
595         timeservers = g_try_new0(char *, entries + 1);
596         if (timeservers) {
597                 for (i = 0, list = option; list; list = list->next, i++)
598                         timeservers[i] = g_strdup(list->data);
599         }
600
601         if (!compare_string_arrays(timeservers, dhcp->timeservers)) {
602                 if (dhcp->timeservers) {
603                         for (i = 0; dhcp->timeservers[i]; i++)
604                                 __connman_service_timeserver_remove(service,
605                                                         dhcp->timeservers[i]);
606                         g_strfreev(dhcp->timeservers);
607                 }
608
609                 dhcp->timeservers = timeservers;
610
611                 for (i = 0; dhcp->timeservers &&
612                                         dhcp->timeservers[i]; i++)
613                         __connman_service_timeserver_append(service,
614                                                         dhcp->timeservers[i]);
615         } else
616                 g_strfreev(timeservers);
617
618         return 0;
619 }
620
621 static GSList *copy_prefixes(GSList *prefixes)
622 {
623         GSList *list, *copy = NULL;
624
625         for (list = prefixes; list; list = list->next)
626                 copy = g_slist_prepend(copy, g_strdup(list->data));
627
628         return copy;
629 }
630
631 /*
632  * Helper struct for doing DAD (duplicate address detection).
633  * It is refcounted and freed after all reply's to neighbor
634  * discovery request are received.
635  */
636 struct own_address {
637         int refcount;
638
639         int ifindex;
640         GDHCPClient *dhcp_client;
641         struct connman_ipconfig *ipconfig;
642         GSList *prefixes;
643         dhcpv6_cb callback;
644
645         GSList *dad_failed;
646         GSList *dad_succeed;
647 };
648
649 static void free_own_address(struct own_address *data)
650 {
651         g_dhcp_client_unref(data->dhcp_client);
652         __connman_ipconfig_unref(data->ipconfig);
653         g_slist_free_full(data->prefixes, free_prefix);
654         g_slist_free_full(data->dad_failed, g_free);
655         g_slist_free_full(data->dad_succeed, g_free);
656
657         g_free(data);
658 }
659
660 static struct own_address *ref_own_address(struct own_address *address)
661 {
662         DBG("%p ref %d", address, address->refcount + 1);
663
664         __sync_fetch_and_add(&address->refcount, 1);
665
666         return address;
667 }
668
669 static void unref_own_address(struct own_address *address)
670 {
671         if (!address)
672                 return;
673
674         DBG("%p ref %d", address, address->refcount - 1);
675
676         if (__sync_fetch_and_sub(&address->refcount, 1) != 1)
677                 return;
678
679         free_own_address(address);
680 }
681
682 static void set_address(int ifindex, struct connman_ipconfig *ipconfig,
683                         GSList *prefixes, char *address)
684 {
685         const char *c_address;
686
687         c_address = __connman_ipconfig_get_local(ipconfig);
688
689         if (address && ((c_address && g_strcmp0(address, c_address) != 0) ||
690                                                                 !c_address)) {
691                 int prefix_len;
692
693                 /* Is this prefix part of the subnet we are suppose to use? */
694                 prefix_len = check_ipv6_addr_prefix(prefixes, address);
695
696 #if defined TIZEN_EXT
697                 char *gateway = g_strdup(__connman_ipconfig_get_gateway(ipconfig));
698 #endif
699                 __connman_ipconfig_address_remove(ipconfig);
700                 __connman_ipconfig_set_local(ipconfig, address);
701                 __connman_ipconfig_set_prefixlen(ipconfig, prefix_len);
702
703                 DBG("new address %s/%d", address, prefix_len);
704
705                 __connman_ipconfig_set_dhcp_address(ipconfig, address);
706 #if defined TIZEN_EXT
707                 DBG("Set gateway %s", gateway);
708                 __connman_ipconfig_set_gateway(ipconfig, gateway);
709                 g_free(gateway);
710 #endif
711                 __connman_service_save(
712                         __connman_service_lookup_from_index(ifindex));
713         }
714 }
715
716
717 /*
718  * Helper struct that is used when waiting a reply to DECLINE message.
719  */
720 struct decline_cb_data {
721         GDHCPClient *dhcp_client;
722         dhcpv6_cb callback;
723         int ifindex;
724         guint timeout;
725 };
726
727 static void decline_reply_callback(struct decline_cb_data *data)
728 {
729         struct connman_network *network;
730         struct connman_service *service;
731
732         service = __connman_service_lookup_from_index(data->ifindex);
733         network = __connman_service_get_network(service);
734
735         if (data->callback)
736                 data->callback(network, CONNMAN_DHCPV6_STATUS_RESTART, NULL);
737
738         g_dhcp_client_unref(data->dhcp_client);
739 }
740
741 static gboolean decline_timeout(gpointer user_data)
742 {
743         struct decline_cb_data *data = user_data;
744
745         DBG("ifindex %d", data->ifindex);
746
747         /*
748          * We ignore all DECLINE replies that are received after the timeout
749          */
750         g_dhcp_client_register_event(data->dhcp_client,
751                                         G_DHCP_CLIENT_EVENT_DECLINE,
752                                         NULL, NULL);
753
754         decline_reply_callback(data);
755
756         g_free(data);
757
758         return FALSE;
759 }
760
761 static void decline_cb(GDHCPClient *dhcp_client, gpointer user_data)
762 {
763         struct decline_cb_data *data = user_data;
764
765         DBG("ifindex %d", data->ifindex);
766
767         g_dhcpv6_client_clear_retransmit(dhcp_client);
768
769         if (data->timeout)
770                 g_source_remove(data->timeout);
771
772         decline_reply_callback(data);
773
774         g_free(data);
775 }
776
777 static int dhcpv6_decline(GDHCPClient *dhcp_client, int ifindex,
778                         dhcpv6_cb callback, GSList *failed)
779 {
780         struct decline_cb_data *data;
781         GList *option;
782         int code;
783
784         DBG("dhcp_client %p", dhcp_client);
785
786         g_dhcp_client_clear_requests(dhcp_client);
787
788         g_dhcpv6_client_clear_send(dhcp_client, G_DHCPV6_ORO);
789
790         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
791         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
792
793         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
794         if (!option) {
795                 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
796                 if (option)
797                         code = G_DHCPV6_IA_TA;
798                 else
799                         return -EINVAL;
800         } else
801                 code = G_DHCPV6_IA_NA;
802
803         g_dhcpv6_client_clear_send(dhcp_client, code);
804
805         g_dhcpv6_client_set_ias(dhcp_client, ifindex, code, NULL, NULL,
806                                 failed);
807
808         clear_callbacks(dhcp_client);
809
810         data = g_try_new(struct decline_cb_data, 1);
811         if (!data)
812                 return -ENOMEM;
813
814         data->ifindex = ifindex;
815         data->callback = callback;
816         data->dhcp_client = g_dhcp_client_ref(dhcp_client);
817         data->timeout = g_timeout_add(DEC_TIMEOUT, decline_timeout, data);
818
819         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_DECLINE,
820                                 decline_cb, data);
821
822         return g_dhcp_client_start(dhcp_client, NULL);
823 }
824
825 static void dad_reply(struct nd_neighbor_advert *reply,
826                 unsigned int length, struct in6_addr *addr, void *user_data)
827 {
828         struct own_address *data = user_data;
829         GSList *list;
830         char address[INET6_ADDRSTRLEN];
831         enum __connman_dhcpv6_status status = CONNMAN_DHCPV6_STATUS_FAIL;
832
833         inet_ntop(AF_INET6, addr, address, INET6_ADDRSTRLEN);
834
835         DBG("user %p reply %p len %d address %s index %d data %p", user_data,
836                 reply, length, address, data->ifindex, data);
837
838         if (!reply) {
839                 if (length == 0)
840                         DBG("DAD succeed for %s", address);
841                 else
842                         DBG("DAD cannot be done for %s", address);
843
844                 data->dad_succeed = g_slist_prepend(data->dad_succeed,
845                                                 g_strdup(address));
846
847         } else {
848                 DBG("DAD failed for %s", address);
849
850                 data->dad_failed = g_slist_prepend(data->dad_failed,
851                                                 g_strdup(address));
852         }
853
854         /*
855          * If refcount == 1 then we got the final reply and can continue.
856          */
857         if (data->refcount > 1)
858                 return;
859
860         for (list = data->dad_succeed; list; list = list->next)
861                 set_address(data->ifindex, data->ipconfig, data->prefixes,
862                                                                 list->data);
863
864         if (data->dad_failed) {
865                 dhcpv6_decline(data->dhcp_client, data->ifindex,
866                         data->callback, data->dad_failed);
867         } else {
868                 if (data->dad_succeed)
869                         status = CONNMAN_DHCPV6_STATUS_SUCCEED;
870
871                 if (data->callback) {
872                         struct connman_network *network;
873                         struct connman_service *service;
874
875                         service = __connman_service_lookup_from_index(
876                                                                 data->ifindex);
877                         network = __connman_service_get_network(service);
878                         if (network)
879                                 data->callback(network, status, NULL);
880                 }
881         }
882
883         unref_own_address(data);
884 }
885
886 /*
887  * Is the kernel configured to do DAD? If 0, then do not do DAD.
888  * See also RFC 4862 chapter 5.4 about DupAddrDetectTransmits
889  */
890 static int dad_transmits(int ifindex)
891 {
892         char name[IF_NAMESIZE];
893         gchar *path;
894         int value = 1;
895         FILE *f;
896
897         if (!if_indextoname(ifindex, name))
898                 return value;
899
900         path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/dad_transmits",
901                                                                 name);
902
903         if (!path)
904                 return value;
905
906         f = fopen(path, "r");
907
908         g_free(path);
909
910         if (f) {
911                 if (fscanf(f, "%d", &value) < 0)
912                         value = 1;
913
914                 fclose(f);
915         }
916
917         return value;
918 }
919
920 static void do_dad(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
921 {
922         struct connman_service *service;
923         struct connman_ipconfig *ipconfig;
924         int ifindex;
925         GList *option, *list;
926         struct own_address *user_data;
927
928         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
929         if (!option)
930                 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
931
932         /*
933          * Even if we didn't had any addresses, just try to set
934          * the other options.
935          */
936         set_other_addresses(dhcp_client, dhcp);
937
938         if (!option) {
939                 DBG("Skip DAD as no addresses found in reply");
940                 if (dhcp->callback)
941                         dhcp->callback(dhcp->network,
942                                         CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
943
944                 return;
945         }
946
947         ifindex = connman_network_get_index(dhcp->network);
948
949         DBG("index %d", ifindex);
950
951         service = connman_service_lookup_from_network(dhcp->network);
952         if (!service) {
953                 connman_error("Can not lookup service for index %d", ifindex);
954                 goto error;
955         }
956
957         ipconfig = __connman_service_get_ip6config(service);
958         if (!ipconfig) {
959                 connman_error("Could not lookup ip6config for index %d",
960                                                                 ifindex);
961                 goto error;
962         }
963
964         if (!dad_transmits(ifindex)) {
965                 DBG("Skip DAD because of kernel configuration");
966
967                 for (list = option; list; list = list->next)
968                         set_address(ifindex, ipconfig, dhcp->prefixes,
969                                                         option->data);
970
971                 if (dhcp->callback)
972                         dhcp->callback(dhcp->network,
973                                         CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
974
975                 return;
976         }
977
978         if (g_list_length(option) == 0) {
979                 DBG("No addresses when doing DAD");
980                 if (dhcp->callback)
981                         dhcp->callback(dhcp->network,
982                                         CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
983
984                 return;
985         }
986
987         user_data = g_try_new0(struct own_address, 1);
988         if (!user_data)
989                 goto error;
990
991         user_data->refcount = 0;
992         user_data->ifindex = ifindex;
993         user_data->dhcp_client = g_dhcp_client_ref(dhcp_client);
994         user_data->ipconfig = __connman_ipconfig_ref(ipconfig);
995         user_data->prefixes = copy_prefixes(dhcp->prefixes);
996         user_data->callback = dhcp->callback;
997
998         /*
999          * We send one neighbor discovery request / address
1000          * and after all checks are done, then report the status
1001          * via dhcp callback.
1002          */
1003
1004         for (list = option; list; list = list->next) {
1005                 char *address = option->data;
1006                 struct in6_addr addr;
1007                 int ret;
1008
1009                 ref_own_address(user_data);
1010
1011                 if (inet_pton(AF_INET6, address, &addr) < 0) {
1012                         DBG("Invalid IPv6 address %s %d/%s", address,
1013                                 -errno, strerror(errno));
1014                         goto fail;
1015                 }
1016
1017                 DBG("user %p address %s client %p ipconfig %p prefixes %p",
1018                         user_data, address,
1019                         user_data->dhcp_client, user_data->ipconfig,
1020                         user_data->prefixes);
1021
1022                 ret = __connman_inet_ipv6_do_dad(ifindex, 1000,
1023                                                 &addr,
1024                                                 dad_reply,
1025                                                 user_data);
1026                 if (ret < 0) {
1027                         DBG("Could not send neighbor solicitation for %s",
1028                                                                 address);
1029                         dad_reply(NULL, -1, &addr, user_data);
1030                 } else {
1031                         DBG("Sent neighbor solicitation %d bytes", ret);
1032                 }
1033         }
1034
1035         return;
1036
1037 fail:
1038         unref_own_address(user_data);
1039
1040 error:
1041         if (dhcp->callback)
1042                 dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
1043                                                                 NULL);
1044 }
1045
1046 static gboolean timeout_request_resend(gpointer user_data)
1047 {
1048         struct connman_dhcpv6 *dhcp = user_data;
1049
1050         if (dhcp->request_count >= REQ_MAX_RC) {
1051                 DBG("max request retry attempts %d", dhcp->request_count);
1052                 dhcp->request_count = 0;
1053                 if (dhcp->callback)
1054                         dhcp->callback(dhcp->network,
1055                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1056                 return FALSE;
1057         }
1058
1059         dhcp->request_count++;
1060
1061         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1062         DBG("request resend RT timeout %d msec", dhcp->RT);
1063         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request_resend, dhcp);
1064
1065         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1066
1067         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1068
1069         return FALSE;
1070 }
1071
1072 static gboolean request_resend(gpointer user_data)
1073 {
1074         struct connman_dhcpv6 *dhcp = user_data;
1075
1076         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1077
1078         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1079         DBG("request resend RT timeout %d msec", dhcp->RT);
1080         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request_resend, dhcp);
1081
1082         dhcpv6_request(dhcp, true);
1083
1084         return FALSE;
1085 }
1086
1087 static void do_resend_request(struct connman_dhcpv6 *dhcp)
1088 {
1089         if (dhcp->request_count >= REQ_MAX_RC) {
1090                 DBG("max request retry attempts %d", dhcp->request_count);
1091                 dhcp->request_count = 0;
1092                 if (dhcp->callback)
1093                         dhcp->callback(dhcp->network,
1094                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1095                 return;
1096         }
1097
1098         dhcp->request_count++;
1099
1100         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1101         DBG("resending request after %d msec", dhcp->RT);
1102         dhcp->timeout = g_timeout_add(dhcp->RT, request_resend, dhcp);
1103 }
1104
1105 static void re_cb(enum request_type req_type, GDHCPClient *dhcp_client,
1106                 gpointer user_data)
1107 {
1108         struct connman_dhcpv6 *dhcp = user_data;
1109         uint16_t status;
1110
1111         clear_timer(dhcp);
1112
1113         status = g_dhcpv6_client_get_status(dhcp_client);
1114
1115         DBG("dhcpv6 cb msg %p status %d", dhcp, status);
1116
1117         /*
1118          * RFC 3315, 18.1.8 handle the resend if error
1119          */
1120         if (status  == G_DHCPV6_ERROR_BINDING) {
1121                 dhcpv6_request(dhcp, false);
1122         } else if (status  == G_DHCPV6_ERROR_MCAST) {
1123                 switch (req_type) {
1124                 case REQ_REQUEST:
1125                         dhcpv6_request(dhcp, true);
1126                         break;
1127                 case REQ_REBIND:
1128                         dhcpv6_rebind(dhcp);
1129                         break;
1130                 case REQ_RENEW:
1131                         dhcpv6_renew(dhcp);
1132                         break;
1133                 }
1134         } else if (status  == G_DHCPV6_ERROR_LINK) {
1135                 if (req_type == REQ_REQUEST) {
1136                         g_dhcp_client_unref(dhcp->dhcp_client);
1137                         start_solicitation(dhcp);
1138                 } else {
1139                         if (dhcp->callback)
1140                                 dhcp->callback(dhcp->network,
1141                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1142                 }
1143         } else if (status  == G_DHCPV6_ERROR_FAILURE) {
1144                 if (req_type == REQ_REQUEST) {
1145                         /* Rate limit the resend of request message */
1146                         do_resend_request(dhcp);
1147                 } else {
1148                         if (dhcp->callback)
1149                                 dhcp->callback(dhcp->network,
1150                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1151                 }
1152         } else {
1153
1154                 /*
1155                  * If we did not got any addresses, then re-send
1156                  * a request.
1157                  */
1158                 GList *option;
1159
1160                 option = g_dhcp_client_get_option(dhcp->dhcp_client,
1161                                                 G_DHCPV6_IA_NA);
1162                 if (!option) {
1163                         option = g_dhcp_client_get_option(dhcp->dhcp_client,
1164                                                         G_DHCPV6_IA_TA);
1165                         if (!option) {
1166                                 switch (req_type) {
1167                                 case REQ_REQUEST:
1168                                         do_resend_request(dhcp);
1169                                         break;
1170                                 case REQ_REBIND:
1171                                         dhcpv6_rebind(dhcp);
1172                                         break;
1173                                 case REQ_RENEW:
1174                                         dhcpv6_renew(dhcp);
1175                                         break;
1176                                 }
1177                                 return;
1178                         }
1179                 }
1180
1181                 if (status == G_DHCPV6_ERROR_SUCCESS)
1182                         do_dad(dhcp_client, dhcp);
1183                 else if (dhcp->callback)
1184                         dhcp->callback(dhcp->network,
1185                                 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1186         }
1187 }
1188
1189 static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data)
1190 {
1191         DBG("");
1192
1193         g_dhcpv6_client_reset_request(dhcp_client);
1194         g_dhcpv6_client_clear_retransmit(dhcp_client);
1195
1196         re_cb(REQ_REBIND, dhcp_client, user_data);
1197 }
1198
1199 static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp)
1200 {
1201         GDHCPClient *dhcp_client;
1202
1203         DBG("dhcp %p", dhcp);
1204
1205         dhcp_client = dhcp->dhcp_client;
1206
1207         g_dhcp_client_clear_requests(dhcp_client);
1208
1209         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1210         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1211         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1212         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1213
1214         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1215                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1216
1217         g_dhcpv6_client_set_ia(dhcp_client,
1218                         connman_network_get_index(dhcp->network),
1219                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1220                         NULL, NULL, TRUE, NULL);
1221
1222         clear_callbacks(dhcp_client);
1223
1224         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
1225                                         rebind_cb, dhcp);
1226
1227         dhcp->dhcp_client = dhcp_client;
1228
1229         return g_dhcp_client_start(dhcp_client, NULL);
1230 }
1231
1232 static gboolean dhcpv6_restart(gpointer user_data)
1233 {
1234         struct connman_dhcpv6 *dhcp = user_data;
1235
1236         if (dhcp->callback)
1237                 dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
1238                                                                 NULL);
1239
1240         return FALSE;
1241 }
1242
1243 /*
1244  * Check if we need to restart the solicitation procedure. This
1245  * is done if all the addresses have expired. RFC 3315, 18.1.4
1246  */
1247 static int check_restart(struct connman_dhcpv6 *dhcp)
1248 {
1249         time_t current, expired;
1250
1251         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
1252                                 NULL, &expired);
1253
1254         /* infinite lifetime for an DHCPv6 address */
1255         if (expired == 0xffffffff)
1256                 return -EISCONN;
1257
1258         current = time(NULL);
1259
1260         if (current >= expired) {
1261                 DBG("expired by %d secs", (int)(current - expired));
1262
1263                 g_idle_add(dhcpv6_restart, dhcp);
1264
1265                 return -ETIMEDOUT;
1266         }
1267
1268         return 0;
1269 }
1270
1271 static gboolean timeout_rebind(gpointer user_data)
1272 {
1273         struct connman_dhcpv6 *dhcp = user_data;
1274
1275         if (check_restart(dhcp) < 0)
1276                 return FALSE;
1277
1278         dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
1279
1280         DBG("rebind RT timeout %d msec", dhcp->RT);
1281
1282         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1283
1284         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1285
1286         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1287
1288         return FALSE;
1289 }
1290
1291 static gboolean start_rebind(gpointer user_data)
1292 {
1293         struct connman_dhcpv6 *dhcp = user_data;
1294
1295         if (check_restart(dhcp) < 0)
1296                 return FALSE;
1297
1298         dhcp->RT = initial_rt(REB_TIMEOUT);
1299
1300         DBG("rebind initial RT timeout %d msec", dhcp->RT);
1301
1302         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1303
1304         dhcpv6_rebind(dhcp);
1305
1306         return FALSE;
1307 }
1308
1309 static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
1310 {
1311         DBG("");
1312
1313         g_dhcpv6_client_reset_request(dhcp_client);
1314         g_dhcpv6_client_clear_retransmit(dhcp_client);
1315
1316         re_cb(REQ_REQUEST, dhcp_client, user_data);
1317 }
1318
1319 static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
1320                         bool add_addresses)
1321 {
1322         GDHCPClient *dhcp_client;
1323         uint32_t T1, T2;
1324
1325         DBG("dhcp %p add %d", dhcp, add_addresses);
1326
1327         dhcp_client = dhcp->dhcp_client;
1328
1329         g_dhcp_client_clear_requests(dhcp_client);
1330
1331         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1332         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1333         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1334         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1335         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1336
1337         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1338                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1339
1340         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1341         g_dhcpv6_client_set_ia(dhcp_client,
1342                         connman_network_get_index(dhcp->network),
1343                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1344                         &T1, &T2, add_addresses, NULL);
1345
1346         clear_callbacks(dhcp_client);
1347
1348         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
1349                                         request_cb, dhcp);
1350
1351         dhcp->dhcp_client = dhcp_client;
1352
1353         return g_dhcp_client_start(dhcp_client, NULL);
1354 }
1355
1356 static gboolean timeout_request(gpointer user_data)
1357 {
1358         struct connman_dhcpv6 *dhcp = user_data;
1359
1360         if (dhcp->request_count >= REQ_MAX_RC) {
1361                 DBG("max request retry attempts %d", dhcp->request_count);
1362                 dhcp->request_count = 0;
1363                 if (dhcp->callback)
1364                         dhcp->callback(dhcp->network,
1365                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1366                 return FALSE;
1367         }
1368
1369         dhcp->request_count++;
1370
1371         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1372         DBG("request RT timeout %d msec", dhcp->RT);
1373         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1374
1375         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1376
1377         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1378
1379         return FALSE;
1380 }
1381
1382 static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
1383 {
1384         DBG("");
1385
1386         g_dhcpv6_client_reset_request(dhcp_client);
1387         g_dhcpv6_client_clear_retransmit(dhcp_client);
1388
1389         re_cb(REQ_RENEW, dhcp_client, user_data);
1390 }
1391
1392 static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
1393 {
1394         GDHCPClient *dhcp_client;
1395         uint32_t T1, T2;
1396
1397         DBG("dhcp %p", dhcp);
1398
1399         dhcp_client = dhcp->dhcp_client;
1400
1401         g_dhcp_client_clear_requests(dhcp_client);
1402
1403         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1404         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1405         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1406         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1407         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1408
1409         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1410                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1411
1412         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1413         g_dhcpv6_client_set_ia(dhcp_client,
1414                         connman_network_get_index(dhcp->network),
1415                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1416                         &T1, &T2, TRUE, NULL);
1417
1418         clear_callbacks(dhcp_client);
1419
1420         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
1421                                         renew_cb, dhcp);
1422
1423         dhcp->dhcp_client = dhcp_client;
1424
1425         return g_dhcp_client_start(dhcp_client, NULL);
1426 }
1427
1428 static gboolean timeout_renew(gpointer user_data)
1429 {
1430         struct connman_dhcpv6 *dhcp = user_data;
1431         time_t last_rebind, current;
1432         uint32_t T2;
1433
1434         if (check_restart(dhcp) < 0)
1435                 return FALSE;
1436
1437         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, &T2,
1438                                 &last_rebind, NULL);
1439         current = time(NULL);
1440         if ((unsigned)current >= (unsigned)last_rebind + T2) {
1441                 /*
1442                  * Do rebind instead if past T2
1443                  */
1444                 start_rebind(dhcp);
1445                 return FALSE;
1446         }
1447
1448         dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
1449
1450         DBG("renew RT timeout %d msec", dhcp->RT);
1451
1452         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1453
1454         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1455
1456         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1457
1458         return FALSE;
1459 }
1460
1461 static gboolean start_renew(gpointer user_data)
1462 {
1463         struct connman_dhcpv6 *dhcp = user_data;
1464
1465         dhcp->RT = initial_rt(REN_TIMEOUT);
1466
1467         DBG("renew initial RT timeout %d msec", dhcp->RT);
1468
1469         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1470
1471         dhcpv6_renew(dhcp);
1472
1473         return FALSE;
1474 }
1475
1476 int __connman_dhcpv6_start_renew(struct connman_network *network,
1477                                                         dhcpv6_cb callback)
1478 {
1479         struct connman_dhcpv6 *dhcp;
1480         uint32_t T1, T2, delta;
1481         time_t started, current, expired;
1482
1483         dhcp = g_hash_table_lookup(network_table, network);
1484         if (!dhcp)
1485                 return -ENOENT;
1486
1487         DBG("network %p dhcp %p", network, dhcp);
1488
1489         clear_timer(dhcp);
1490
1491         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
1492                                 &started, &expired);
1493
1494         current = time(NULL);
1495
1496         DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
1497                 (unsigned long)expired, current, started);
1498
1499         if (T1 == 0xffffffff)
1500                 /* RFC 3315, 22.4 */
1501                 return 0;
1502
1503         if (T1 == 0) {
1504                 /* RFC 3315, 22.4
1505                  * Client can choose the timeout.
1506                  */
1507                 T1 = (expired - started) / 2;
1508                 T2 = (expired - started) / 10 * 8;
1509         }
1510
1511         dhcp->callback = callback;
1512
1513         /* RFC 3315, 18.1.4, start solicit if expired */
1514         if (check_restart(dhcp) < 0)
1515                 return 0;
1516
1517         if (T2 != 0xffffffff && T2 > 0) {
1518                 if ((unsigned)current >= (unsigned)started + T2) {
1519                         /* RFC 3315, chapter 18.1.3, start rebind */
1520                         DBG("start rebind immediately");
1521
1522                         dhcp->timeout = g_idle_add(start_rebind, dhcp);
1523
1524                 } else if ((unsigned)current < (unsigned)started + T1) {
1525                         delta = started + T1 - current;
1526                         DBG("renew after %d secs", delta);
1527
1528                         dhcp->timeout = g_timeout_add_seconds(delta,
1529                                         start_renew, dhcp);
1530                 } else {
1531                         delta = started + T2 - current;
1532                         DBG("rebind after %d secs", delta);
1533
1534                         dhcp->timeout = g_timeout_add_seconds(delta,
1535                                         start_rebind, dhcp);
1536                 }
1537         }
1538
1539         return 0;
1540 }
1541
1542 static void release_cb(GDHCPClient *dhcp_client, gpointer user_data)
1543 {
1544         DBG("");
1545 }
1546
1547 int __connman_dhcpv6_start_release(struct connman_network *network,
1548                                 dhcpv6_cb callback)
1549 {
1550         struct connman_dhcpv6 *dhcp;
1551         GDHCPClient *dhcp_client;
1552
1553         if (!network_table)
1554                 return 0;   /* we are already released */
1555
1556         dhcp = g_hash_table_lookup(network_table, network);
1557         if (!dhcp)
1558                 return -ENOENT;
1559
1560         DBG("network %p dhcp %p client %p stateless %d", network, dhcp,
1561                                         dhcp->dhcp_client, dhcp->stateless);
1562
1563         if (dhcp->stateless)
1564                 return -EINVAL;
1565
1566         clear_timer(dhcp);
1567
1568         dhcp_client = dhcp->dhcp_client;
1569         if (!dhcp_client) {
1570                 /*
1571                  * We had started the DHCPv6 handshaking i.e., we have called
1572                  * __connman_dhcpv6_start() but it has not yet sent
1573                  * a solicitation message to server. This means that we do not
1574                  * have DHCPv6 configured yet so we can just quit here.
1575                  */
1576                 DBG("DHCPv6 was not started");
1577                 return 0;
1578         }
1579
1580         g_dhcp_client_clear_requests(dhcp_client);
1581         g_dhcp_client_clear_values(dhcp_client);
1582
1583         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1584         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1585
1586         g_dhcpv6_client_set_ia(dhcp_client,
1587                         connman_network_get_index(dhcp->network),
1588                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1589                         NULL, NULL, TRUE, NULL);
1590
1591         clear_callbacks(dhcp_client);
1592
1593         /*
1594          * Although we register a callback here we are really not interested in
1595          * the answer because it might take too long time and network code
1596          * might be in the middle of the disconnect.
1597          * So we just inform the server that we are done with the addresses
1598          * but ignore the reply from server. This is allowed by RFC 3315
1599          * chapter 18.1.6.
1600          */
1601         g_dhcp_client_register_event(dhcp_client,
1602                                 G_DHCP_CLIENT_EVENT_RELEASE,
1603                                 release_cb, NULL);
1604
1605         dhcp->dhcp_client = dhcp_client;
1606
1607         return g_dhcp_client_start(dhcp_client, NULL);
1608 }
1609
1610 static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
1611 {
1612         DBG("dhcp %p", dhcp);
1613
1614         clear_timer(dhcp);
1615
1616         dhcpv6_free(dhcp);
1617
1618         if (!dhcp->dhcp_client)
1619                 return 0;
1620
1621         g_dhcp_client_stop(dhcp->dhcp_client);
1622         g_dhcp_client_unref(dhcp->dhcp_client);
1623
1624         dhcp->dhcp_client = NULL;
1625
1626         return 0;
1627 }
1628
1629 static void remove_network(gpointer user_data)
1630 {
1631         struct connman_dhcpv6 *dhcp = user_data;
1632
1633         DBG("dhcp %p", dhcp);
1634
1635         dhcpv6_release(dhcp);
1636
1637         g_free(dhcp);
1638 }
1639
1640 static gboolean timeout_info_req(gpointer user_data)
1641 {
1642         struct connman_dhcpv6 *dhcp = user_data;
1643
1644         dhcp->RT = calc_delay(dhcp->RT, INF_MAX_RT);
1645
1646         DBG("info RT timeout %d msec", dhcp->RT);
1647
1648         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1649
1650         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1651
1652         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1653
1654         return FALSE;
1655 }
1656
1657 static gboolean start_info_req(gpointer user_data)
1658 {
1659         struct connman_dhcpv6 *dhcp = user_data;
1660
1661         /* Set the retransmission timeout, RFC 3315 chapter 14 */
1662         dhcp->RT = initial_rt(INF_TIMEOUT);
1663
1664         DBG("info initial RT timeout %d msec", dhcp->RT);
1665
1666         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1667
1668         dhcpv6_info_request(dhcp);
1669
1670         return FALSE;
1671 }
1672
1673 int __connman_dhcpv6_start_info(struct connman_network *network,
1674                                 dhcpv6_cb callback)
1675 {
1676         struct connman_dhcpv6 *dhcp;
1677         int delay;
1678         uint64_t rand;
1679
1680         DBG("");
1681
1682         if (network_table) {
1683                 dhcp = g_hash_table_lookup(network_table, network);
1684                 if (dhcp && dhcp->started)
1685                         return -EBUSY;
1686         }
1687
1688         dhcp = g_try_new0(struct connman_dhcpv6, 1);
1689         if (!dhcp)
1690                 return -ENOMEM;
1691
1692         dhcp->network = network;
1693         dhcp->callback = callback;
1694         dhcp->stateless = true;
1695         dhcp->started = true;
1696
1697         connman_network_ref(network);
1698
1699         DBG("replace network %p dhcp %p", network, dhcp);
1700
1701         g_hash_table_replace(network_table, network, dhcp);
1702
1703         /* Initial timeout, RFC 3315, 18.1.5 */
1704         __connman_util_get_random(&rand);
1705         delay = rand % 1000;
1706
1707         dhcp->timeout = g_timeout_add(delay, start_info_req, dhcp);
1708
1709         return 0;
1710 }
1711
1712 static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data)
1713 {
1714         struct connman_dhcpv6 *dhcp = user_data;
1715
1716         DBG("dhcpv6 advertise msg %p", dhcp);
1717
1718         clear_timer(dhcp);
1719
1720         g_dhcpv6_client_clear_retransmit(dhcp_client);
1721
1722         if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1723                 if (dhcp->callback)
1724                         dhcp->callback(dhcp->network,
1725                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1726                 return;
1727         }
1728
1729         dhcp->RT = initial_rt(REQ_TIMEOUT);
1730         DBG("request initial RT timeout %d msec", dhcp->RT);
1731         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1732
1733         dhcp->request_count = 1;
1734
1735         dhcpv6_request(dhcp, true);
1736 }
1737
1738 static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)
1739 {
1740         /* We get here if server supports rapid commit */
1741         struct connman_dhcpv6 *dhcp = user_data;
1742
1743         DBG("dhcpv6 solicitation msg %p", dhcp);
1744
1745         clear_timer(dhcp);
1746
1747         g_dhcpv6_client_clear_retransmit(dhcp_client);
1748
1749         if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1750                 if (dhcp->callback)
1751                         dhcp->callback(dhcp->network,
1752                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1753                 return;
1754         }
1755
1756         do_dad(dhcp_client, dhcp);
1757 }
1758
1759 static gboolean timeout_solicitation(gpointer user_data)
1760 {
1761         struct connman_dhcpv6 *dhcp = user_data;
1762
1763         dhcp->RT = calc_delay(dhcp->RT, SOL_MAX_RT);
1764
1765         DBG("solicit RT timeout %d msec", dhcp->RT);
1766
1767         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1768
1769         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1770
1771         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1772
1773         return FALSE;
1774 }
1775
1776 static int dhcpv6_solicitation(struct connman_dhcpv6 *dhcp)
1777 {
1778         struct connman_service *service;
1779 #if !defined TIZEN_EXT
1780         struct connman_ipconfig *ipconfig_ipv6;
1781 #endif
1782         GDHCPClient *dhcp_client;
1783         GDHCPClientError error;
1784         int index, ret;
1785
1786         DBG("dhcp %p", dhcp);
1787
1788         index = connman_network_get_index(dhcp->network);
1789
1790         dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
1791         if (error != G_DHCP_CLIENT_ERROR_NONE) {
1792                 clear_timer(dhcp);
1793                 return -EINVAL;
1794         }
1795
1796 #if !defined TIZEN_EXT
1797         if (getenv("CONNMAN_DHCPV6_DEBUG"))
1798 #endif
1799                 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
1800
1801         service = connman_service_lookup_from_network(dhcp->network);
1802         if (!service) {
1803                 clear_timer(dhcp);
1804                 g_dhcp_client_unref(dhcp_client);
1805                 return -EINVAL;
1806         }
1807
1808         ret = set_duid(service, dhcp->network, dhcp_client, index);
1809         if (ret < 0) {
1810                 clear_timer(dhcp);
1811                 g_dhcp_client_unref(dhcp_client);
1812                 return ret;
1813         }
1814
1815         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1816         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
1817         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1818         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1819         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1820
1821         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1822                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1823
1824 #if defined TIZEN_EXT
1825         /**
1826           When privacy extension is enabled then connman requests
1827           OPTION_IA_TA (4) from DHCPv6 server. This option is used to request
1828           temporary IPv6 address from DHCPv6 server but we found that DHCPv6
1829           server never provided temporary IPv6 address and connman resend dhcpv6
1830           requests. So always set OPTION_IA_NA in dhcpv6 request to get IPv6
1831           address from DHCPv6 server.
1832          */
1833         dhcp->use_ta = FALSE;
1834 #else
1835         ipconfig_ipv6 = __connman_service_get_ip6config(service);
1836         dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
1837 #endif
1838
1839         g_dhcpv6_client_set_ia(dhcp_client, index,
1840                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1841                         NULL, NULL, FALSE, NULL);
1842
1843         clear_callbacks(dhcp_client);
1844
1845         g_dhcp_client_register_event(dhcp_client,
1846                                 G_DHCP_CLIENT_EVENT_SOLICITATION,
1847                                 solicitation_cb, dhcp);
1848
1849         g_dhcp_client_register_event(dhcp_client,
1850                                 G_DHCP_CLIENT_EVENT_ADVERTISE,
1851                                 advertise_cb, dhcp);
1852
1853         dhcp->dhcp_client = dhcp_client;
1854
1855         return g_dhcp_client_start(dhcp_client, NULL);
1856 }
1857
1858 static gboolean start_solicitation(gpointer user_data)
1859 {
1860         struct connman_dhcpv6 *dhcp = user_data;
1861
1862         /* Set the retransmission timeout, RFC 3315 chapter 14 */
1863         dhcp->RT = initial_rt(SOL_TIMEOUT);
1864
1865         DBG("solicit initial RT timeout %d msec", dhcp->RT);
1866
1867         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1868
1869         dhcpv6_solicitation(dhcp);
1870
1871         return FALSE;
1872 }
1873
1874 int __connman_dhcpv6_start(struct connman_network *network,
1875                                 GSList *prefixes, dhcpv6_cb callback)
1876 {
1877         struct connman_service *service;
1878         struct connman_dhcpv6 *dhcp;
1879         int delay;
1880         uint64_t rand;
1881
1882         DBG("");
1883
1884         if (network_table) {
1885                 dhcp = g_hash_table_lookup(network_table, network);
1886                 if (dhcp && dhcp->started)
1887                         return -EBUSY;
1888         }
1889
1890         service = connman_service_lookup_from_network(network);
1891         if (!service)
1892                 return -EINVAL;
1893
1894         dhcp = g_try_new0(struct connman_dhcpv6, 1);
1895         if (!dhcp)
1896                 return -ENOMEM;
1897
1898         dhcp->network = network;
1899         dhcp->callback = callback;
1900         dhcp->prefixes = prefixes;
1901         dhcp->started = true;
1902
1903         connman_network_ref(network);
1904
1905         DBG("replace network %p dhcp %p", network, dhcp);
1906
1907         g_hash_table_replace(network_table, network, dhcp);
1908
1909         /* Initial timeout, RFC 3315, 17.1.2 */
1910         __connman_util_get_random(&rand);
1911         delay = rand % 1000;
1912
1913         /*
1914          * Start from scratch.
1915          * RFC 3315, chapter 17.1.2 Solicitation message
1916          *
1917          * Note that we do not send CONFIRM message here as it does
1918          * not make much sense because we do not save expiration time
1919          * so we cannot really know how long the saved address is valid
1920          * anyway. The reply to CONFIRM message does not send
1921          * expiration times back to us. Because of this we need to
1922          * start using SOLICITATION anyway.
1923          */
1924         dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
1925
1926         return 0;
1927 }
1928
1929 void __connman_dhcpv6_stop(struct connman_network *network)
1930 {
1931         DBG("");
1932
1933         if (!network_table)
1934                 return;
1935
1936         if (g_hash_table_remove(network_table, network))
1937                 connman_network_unref(network);
1938 }
1939
1940 static int save_prefixes(struct connman_ipconfig *ipconfig,
1941                         GSList *prefixes)
1942 {
1943         GSList *list;
1944         int i = 0, count = g_slist_length(prefixes);
1945         char **array;
1946
1947         if (count == 0)
1948                 return 0;
1949
1950         array = g_try_new0(char *, count + 1);
1951         if (!array)
1952                 return -ENOMEM;
1953
1954         for (list = prefixes; list; list = list->next) {
1955                 char *elem, addr_str[INET6_ADDRSTRLEN];
1956                 GDHCPIAPrefix *prefix = list->data;
1957
1958                 elem = g_strdup_printf("%s/%d", inet_ntop(AF_INET6,
1959                                 &prefix->prefix, addr_str, INET6_ADDRSTRLEN),
1960                                 prefix->prefixlen);
1961                 if (!elem) {
1962                         g_strfreev(array);
1963                         return -ENOMEM;
1964                 }
1965
1966                 array[i++] = elem;
1967         }
1968
1969         __connman_ipconfig_set_dhcpv6_prefixes(ipconfig, array);
1970         return 0;
1971 }
1972
1973 static GSList *load_prefixes(struct connman_ipconfig *ipconfig)
1974 {
1975         int i;
1976         GSList *list = NULL;
1977         char **array =  __connman_ipconfig_get_dhcpv6_prefixes(ipconfig);
1978
1979         if (!array)
1980                 return NULL;
1981
1982         for (i = 0; array[i]; i++) {
1983                 GDHCPIAPrefix *prefix;
1984                 long int value;
1985                 char *ptr, **elems = g_strsplit(array[i], "/", 0);
1986
1987                 if (!elems)
1988                         return list;
1989
1990                 value = strtol(elems[1], &ptr, 10);
1991                 if (ptr != elems[1] && *ptr == '\0' && value <= 128) {
1992                         struct in6_addr addr;
1993
1994                         if (inet_pton(AF_INET6, elems[0], &addr) == 1) {
1995                                 prefix = g_try_new0(GDHCPIAPrefix, 1);
1996                                 if (!prefix) {
1997                                         g_strfreev(elems);
1998                                         return list;
1999                                 }
2000                                 memcpy(&prefix->prefix, &addr,
2001                                         sizeof(struct in6_addr));
2002                                 prefix->prefixlen = value;
2003
2004                                 list = g_slist_prepend(list, prefix);
2005                         }
2006                 }
2007
2008                 g_strfreev(elems);
2009         }
2010
2011         return list;
2012 }
2013
2014 static GDHCPIAPrefix *copy_prefix(gpointer data)
2015 {
2016         GDHCPIAPrefix *copy, *prefix = data;
2017
2018         copy = g_try_new(GDHCPIAPrefix, 1);
2019         if (!copy)
2020                 return NULL;
2021
2022         memcpy(copy, prefix, sizeof(GDHCPIAPrefix));
2023
2024         return copy;
2025 }
2026
2027 static GSList *copy_and_convert_prefixes(GList *prefixes)
2028 {
2029         GSList *copy = NULL;
2030         GList *list;
2031
2032         for (list = prefixes; list; list = list->next)
2033                 copy = g_slist_prepend(copy, copy_prefix(list->data));
2034
2035         return copy;
2036 }
2037
2038 static int set_prefixes(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
2039 {
2040         if (dhcp->prefixes)
2041                 g_slist_free_full(dhcp->prefixes, free_prefix);
2042
2043         dhcp->prefixes =
2044                 copy_and_convert_prefixes(g_dhcp_client_get_option(dhcp_client,
2045                                                         G_DHCPV6_IA_PD));
2046
2047         DBG("Got %d prefix", g_slist_length(dhcp->prefixes));
2048
2049         if (dhcp->callback) {
2050                 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
2051                 if (status == G_DHCPV6_ERROR_NO_PREFIX)
2052                         dhcp->callback(dhcp->network,
2053                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2054                 else {
2055                         struct connman_service *service;
2056                         struct connman_ipconfig *ipconfig;
2057                         int ifindex = connman_network_get_index(dhcp->network);
2058
2059                         service = __connman_service_lookup_from_index(ifindex);
2060                         if (service) {
2061                                 ipconfig = __connman_service_get_ip6config(
2062                                                                 service);
2063                                 save_prefixes(ipconfig, dhcp->prefixes);
2064                                 __connman_service_save(service);
2065                         }
2066
2067                         dhcp->callback(dhcp->network,
2068                                 CONNMAN_DHCPV6_STATUS_SUCCEED, dhcp->prefixes);
2069                 }
2070         } else {
2071                 g_slist_free_full(dhcp->prefixes, free_prefix);
2072                 dhcp->prefixes = NULL;
2073         }
2074
2075         return 0;
2076 }
2077
2078 static void re_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2079 {
2080         struct connman_dhcpv6 *dhcp = user_data;
2081         uint16_t status;
2082         int ret;
2083
2084         status = g_dhcpv6_client_get_status(dhcp_client);
2085
2086         DBG("dhcpv6 cb msg %p status %d", dhcp, status);
2087
2088         if (status  == G_DHCPV6_ERROR_BINDING) {
2089                 /* RFC 3315, 18.1.8 */
2090                 dhcpv6_pd_request(dhcp);
2091         } else {
2092                 ret = set_prefixes(dhcp_client, dhcp);
2093                 if (ret < 0) {
2094                         if (dhcp->callback)
2095                                 dhcp->callback(dhcp->network,
2096                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2097                         return;
2098                 }
2099         }
2100 }
2101
2102 static void rebind_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2103 {
2104         DBG("");
2105
2106         g_dhcpv6_client_clear_retransmit(dhcp_client);
2107
2108         re_pd_cb(dhcp_client, user_data);
2109 }
2110
2111 static GDHCPClient *create_pd_client(struct connman_dhcpv6 *dhcp, int *err)
2112 {
2113         GDHCPClient *dhcp_client;
2114         GDHCPClientError error;
2115         struct connman_service *service;
2116         int index, ret;
2117         uint32_t iaid;
2118
2119         index = connman_network_get_index(dhcp->network);
2120
2121         dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
2122         if (error != G_DHCP_CLIENT_ERROR_NONE) {
2123                 *err = -EINVAL;
2124                 return NULL;
2125         }
2126
2127 #if !defined TIZEN_EXT
2128         if (getenv("CONNMAN_DHCPV6_DEBUG"))
2129 #endif
2130                 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6:PD");
2131
2132         service = connman_service_lookup_from_network(dhcp->network);
2133         if (!service) {
2134                 g_dhcp_client_unref(dhcp_client);
2135                 *err = -EINVAL;
2136                 return NULL;
2137         }
2138
2139         ret = set_duid(service, dhcp->network, dhcp_client, index);
2140         if (ret < 0) {
2141                 g_dhcp_client_unref(dhcp_client);
2142                 *err = ret;
2143                 return NULL;
2144         }
2145
2146         g_dhcpv6_client_create_iaid(dhcp_client, index, (unsigned char *)&iaid);
2147         g_dhcpv6_client_set_iaid(dhcp_client, iaid);
2148
2149         return dhcp_client;
2150 }
2151
2152 static int dhcpv6_pd_rebind(struct connman_dhcpv6 *dhcp)
2153 {
2154         GDHCPClient *dhcp_client;
2155         uint32_t T1, T2;
2156
2157         DBG("dhcp %p", dhcp);
2158
2159         if (!dhcp->dhcp_client) {
2160                 /*
2161                  * We skipped the solicitation phase
2162                  */
2163                 int err;
2164
2165                 dhcp->dhcp_client = create_pd_client(dhcp, &err);
2166                 if (!dhcp->dhcp_client) {
2167                         clear_timer(dhcp);
2168                         return err;
2169                 }
2170         }
2171
2172         dhcp_client = dhcp->dhcp_client;
2173
2174         g_dhcp_client_clear_requests(dhcp_client);
2175
2176         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2177         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2178         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2179
2180         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2181         g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2182
2183         clear_callbacks(dhcp_client);
2184
2185         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
2186                                         rebind_pd_cb, dhcp);
2187
2188         return g_dhcp_client_start(dhcp_client, NULL);
2189 }
2190
2191 static void renew_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2192 {
2193         DBG("");
2194
2195         g_dhcpv6_client_clear_retransmit(dhcp_client);
2196
2197         re_pd_cb(dhcp_client, user_data);
2198 }
2199
2200 static int dhcpv6_pd_renew(struct connman_dhcpv6 *dhcp)
2201 {
2202         GDHCPClient *dhcp_client;
2203         uint32_t T1, T2;
2204
2205         DBG("dhcp %p", dhcp);
2206
2207         dhcp_client = dhcp->dhcp_client;
2208
2209         g_dhcp_client_clear_requests(dhcp_client);
2210
2211         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2212         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2213         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2214         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2215
2216         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2217         g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2218
2219         clear_callbacks(dhcp_client);
2220
2221         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
2222                                         renew_pd_cb, dhcp);
2223
2224         return g_dhcp_client_start(dhcp_client, NULL);
2225 }
2226
2227 /*
2228  * Check if we need to restart the solicitation procedure. This
2229  * is done if all the prefixes have expired.
2230  */
2231 static int check_pd_restart(struct connman_dhcpv6 *dhcp)
2232 {
2233         time_t current, expired;
2234
2235         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
2236                                 NULL, &expired);
2237         current = time(NULL);
2238
2239         if (current > expired) {
2240                 DBG("expired by %d secs", (int)(current - expired));
2241
2242                 g_idle_add(dhcpv6_restart, dhcp);
2243
2244                 return -ETIMEDOUT;
2245         }
2246
2247         return 0;
2248 }
2249
2250 static gboolean timeout_pd_rebind(gpointer user_data)
2251 {
2252         struct connman_dhcpv6 *dhcp = user_data;
2253
2254         if (check_pd_restart(dhcp) < 0)
2255                 return FALSE;
2256
2257         dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
2258
2259         DBG("rebind RT timeout %d msec", dhcp->RT);
2260
2261         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2262
2263         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2264
2265         g_dhcp_client_start(dhcp->dhcp_client, NULL);
2266
2267         return FALSE;
2268 }
2269
2270 static gboolean start_pd_rebind(gpointer user_data)
2271 {
2272         struct connman_dhcpv6 *dhcp = user_data;
2273
2274         if (check_pd_restart(dhcp) < 0)
2275                 return FALSE;
2276
2277         dhcp->RT = initial_rt(REB_TIMEOUT);
2278
2279         DBG("rebind initial RT timeout %d msec", dhcp->RT);
2280
2281         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2282
2283         dhcpv6_pd_rebind(dhcp);
2284
2285         return FALSE;
2286 }
2287
2288 static gboolean timeout_pd_rebind_confirm(gpointer user_data)
2289 {
2290         struct connman_dhcpv6 *dhcp = user_data;
2291
2292         dhcp->RT = calc_delay(dhcp->RT, CNF_MAX_RT);
2293
2294         DBG("rebind with confirm RT timeout %d msec", dhcp->RT);
2295
2296         dhcp->timeout = g_timeout_add(dhcp->RT,
2297                                         timeout_pd_rebind_confirm, dhcp);
2298
2299         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2300
2301         g_dhcp_client_start(dhcp->dhcp_client, NULL);
2302
2303         return FALSE;
2304 }
2305
2306 static gboolean timeout_pd_max_confirm(gpointer user_data)
2307 {
2308         struct connman_dhcpv6 *dhcp = user_data;
2309
2310         dhcp->MRD = 0;
2311
2312         clear_timer(dhcp);
2313
2314         DBG("rebind with confirm max retransmit duration timeout");
2315
2316         g_dhcpv6_client_clear_retransmit(dhcp->dhcp_client);
2317
2318         if (dhcp->callback)
2319                 dhcp->callback(dhcp->network,
2320                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2321
2322         return FALSE;
2323 }
2324
2325 static gboolean start_pd_rebind_with_confirm(gpointer user_data)
2326 {
2327         struct connman_dhcpv6 *dhcp = user_data;
2328
2329         dhcp->RT = initial_rt(CNF_TIMEOUT);
2330
2331         DBG("rebind with confirm initial RT timeout %d msec", dhcp->RT);
2332
2333         dhcp->timeout = g_timeout_add(dhcp->RT,
2334                                         timeout_pd_rebind_confirm, dhcp);
2335         dhcp->MRD = g_timeout_add(CNF_MAX_RD, timeout_pd_max_confirm, dhcp);
2336
2337         dhcpv6_pd_rebind(dhcp);
2338
2339         return FALSE;
2340 }
2341
2342 static gboolean timeout_pd_renew(gpointer user_data)
2343 {
2344         struct connman_dhcpv6 *dhcp = user_data;
2345
2346         if (check_pd_restart(dhcp) < 0)
2347                 return FALSE;
2348
2349         dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
2350
2351         DBG("renew RT timeout %d msec", dhcp->RT);
2352
2353         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
2354
2355         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2356
2357         g_dhcp_client_start(dhcp->dhcp_client, NULL);
2358
2359         return FALSE;
2360 }
2361
2362 static gboolean start_pd_renew(gpointer user_data)
2363 {
2364         struct connman_dhcpv6 *dhcp = user_data;
2365
2366         dhcp->RT = initial_rt(REN_TIMEOUT);
2367
2368         DBG("renew initial RT timeout %d msec", dhcp->RT);
2369
2370         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_renew, dhcp);
2371
2372         dhcpv6_pd_renew(dhcp);
2373
2374         return FALSE;
2375 }
2376
2377 int __connman_dhcpv6_start_pd_renew(struct connman_network *network,
2378                                 dhcpv6_cb callback)
2379 {
2380         struct connman_dhcpv6 *dhcp;
2381         uint32_t T1, T2;
2382         time_t started, current, expired;
2383
2384         dhcp = g_hash_table_lookup(network_pd_table, network);
2385         if (!dhcp)
2386                 return -ENOENT;
2387
2388         DBG("network %p dhcp %p", network, dhcp);
2389
2390         clear_timer(dhcp);
2391
2392         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
2393                                 &started, &expired);
2394
2395         current = time(NULL);
2396
2397         DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
2398                 expired, current, started);
2399
2400         if (T1 == 0xffffffff)
2401                 /* RFC 3633, ch 9 */
2402                 return 0;
2403
2404         if (T1 == 0)
2405                 /* RFC 3633, ch 9
2406                  * Client can choose the timeout.
2407                  */
2408                 T1 = 120;
2409
2410         dhcp->callback = callback;
2411
2412         /* RFC 3315, 18.1.4, start solicit if expired */
2413         if (check_pd_restart(dhcp) < 0)
2414                 return 0;
2415
2416         if (T2 != 0xffffffff && T2 > 0) {
2417                 if ((unsigned)current >= (unsigned)started + T2) {
2418                         /* RFC 3315, chapter 18.1.3, start rebind */
2419                         DBG("rebind after %d secs", T2);
2420
2421                         dhcp->timeout = g_timeout_add_seconds(T2,
2422                                                         start_pd_rebind,
2423                                                         dhcp);
2424
2425                 } else if ((unsigned)current < (unsigned)started + T1) {
2426                         DBG("renew after %d secs", T1);
2427
2428                         dhcp->timeout = g_timeout_add_seconds(T1,
2429                                                         start_pd_renew,
2430                                                         dhcp);
2431                 } else {
2432                         DBG("rebind after %d secs", T2 - T1);
2433
2434                         dhcp->timeout = g_timeout_add_seconds(T2 - T1,
2435                                                         start_pd_rebind,
2436                                                         dhcp);
2437                 }
2438         }
2439
2440         return 0;
2441 }
2442
2443 static void release_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2444 {
2445         DBG("");
2446 }
2447
2448 int __connman_dhcpv6_start_pd_release(struct connman_network *network,
2449                                 dhcpv6_cb callback)
2450 {
2451         struct connman_dhcpv6 *dhcp;
2452         GDHCPClient *dhcp_client;
2453         uint32_t T1, T2;
2454
2455         if (!network_table)
2456                 return 0;   /* we are already released */
2457
2458         dhcp = g_hash_table_lookup(network_pd_table, network);
2459         if (!dhcp)
2460                 return -ENOENT;
2461
2462         DBG("network %p dhcp %p client %p", network, dhcp, dhcp->dhcp_client);
2463
2464         clear_timer(dhcp);
2465
2466         dhcp_client = dhcp->dhcp_client;
2467         if (!dhcp_client) {
2468                 DBG("DHCPv6 PD was not started");
2469                 return 0;
2470         }
2471
2472         g_dhcp_client_clear_requests(dhcp_client);
2473         g_dhcp_client_clear_values(dhcp_client);
2474
2475         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2476         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2477
2478         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2479         g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2480
2481         clear_callbacks(dhcp_client);
2482
2483         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RELEASE,
2484                                         release_pd_cb, dhcp);
2485
2486         dhcp->dhcp_client = dhcp_client;
2487
2488         return g_dhcp_client_start(dhcp_client, NULL);
2489 }
2490
2491 static gboolean timeout_pd_request(gpointer user_data)
2492 {
2493         struct connman_dhcpv6 *dhcp = user_data;
2494
2495         if (dhcp->request_count >= REQ_MAX_RC) {
2496                 DBG("max request retry attempts %d", dhcp->request_count);
2497                 dhcp->request_count = 0;
2498                 if (dhcp->callback)
2499                         dhcp->callback(dhcp->network,
2500                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2501                 return FALSE;
2502         }
2503
2504         dhcp->request_count++;
2505
2506         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
2507         DBG("request RT timeout %d msec", dhcp->RT);
2508         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2509
2510         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2511
2512         g_dhcp_client_start(dhcp->dhcp_client, NULL);
2513
2514         return FALSE;
2515 }
2516
2517 static void request_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2518 {
2519         struct connman_dhcpv6 *dhcp = user_data;
2520         uint16_t status;
2521
2522         DBG("");
2523
2524         g_dhcpv6_client_clear_retransmit(dhcp_client);
2525
2526         status = g_dhcpv6_client_get_status(dhcp_client);
2527
2528         DBG("dhcpv6 pd cb msg %p status %d", dhcp, status);
2529
2530         if (status  == G_DHCPV6_ERROR_BINDING) {
2531                 /* RFC 3315, 18.1.8 */
2532                 dhcpv6_pd_request(dhcp);
2533         } else {
2534                 set_prefixes(dhcp_client, dhcp);
2535         }
2536 }
2537
2538 static int dhcpv6_pd_request(struct connman_dhcpv6 *dhcp)
2539 {
2540         GDHCPClient *dhcp_client;
2541         uint32_t T1 = 0, T2 = 0;
2542
2543         DBG("dhcp %p", dhcp);
2544
2545         dhcp_client = dhcp->dhcp_client;
2546
2547         g_dhcp_client_clear_requests(dhcp_client);
2548
2549         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2550         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2551         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2552         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2553
2554         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2555         g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2556
2557         clear_callbacks(dhcp_client);
2558
2559         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
2560                                         request_pd_cb, dhcp);
2561
2562         return g_dhcp_client_start(dhcp_client, NULL);
2563 }
2564
2565 static void advertise_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2566 {
2567         struct connman_dhcpv6 *dhcp = user_data;
2568
2569         DBG("dhcpv6 advertise pd msg %p", dhcp);
2570
2571         clear_timer(dhcp);
2572
2573         g_dhcpv6_client_clear_retransmit(dhcp_client);
2574
2575         if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
2576                 if (dhcp->callback)
2577                         dhcp->callback(dhcp->network,
2578                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2579                 return;
2580         }
2581
2582         dhcp->RT = initial_rt(REQ_TIMEOUT);
2583         DBG("request initial RT timeout %d msec", dhcp->RT);
2584         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2585
2586         dhcp->request_count = 1;
2587
2588         dhcpv6_pd_request(dhcp);
2589 }
2590
2591 static void solicitation_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2592 {
2593         /*
2594          * This callback is here so that g_dhcp_client_start()
2595          * will enter the proper L3 mode.
2596          */
2597         DBG("DHCPv6 %p solicitation msg received, ignoring it", user_data);
2598 }
2599
2600 static int dhcpv6_pd_solicitation(struct connman_dhcpv6 *dhcp)
2601 {
2602         GDHCPClient *dhcp_client;
2603         int ret;
2604
2605         DBG("dhcp %p", dhcp);
2606
2607         dhcp_client = create_pd_client(dhcp, &ret);
2608         if (!dhcp_client) {
2609                 clear_timer(dhcp);
2610                 return ret;
2611         }
2612
2613         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2614
2615         g_dhcpv6_client_set_pd(dhcp_client, NULL, NULL, NULL);
2616
2617         clear_callbacks(dhcp_client);
2618
2619         g_dhcp_client_register_event(dhcp_client,
2620                                 G_DHCP_CLIENT_EVENT_ADVERTISE,
2621                                 advertise_pd_cb, dhcp);
2622
2623         g_dhcp_client_register_event(dhcp_client,
2624                                 G_DHCP_CLIENT_EVENT_SOLICITATION,
2625                                 solicitation_pd_cb, dhcp);
2626
2627         dhcp->dhcp_client = dhcp_client;
2628
2629         return g_dhcp_client_start(dhcp_client, NULL);
2630 }
2631
2632 static gboolean start_pd_solicitation(gpointer user_data)
2633 {
2634         struct connman_dhcpv6 *dhcp = user_data;
2635
2636         /* Set the retransmission timeout, RFC 3315 chapter 14 */
2637         dhcp->RT = initial_rt(SOL_TIMEOUT);
2638
2639         DBG("solicit initial RT timeout %d msec", dhcp->RT);
2640
2641         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
2642
2643         dhcpv6_pd_solicitation(dhcp);
2644
2645         return FALSE;
2646 }
2647
2648 int __connman_dhcpv6_start_pd(int index, GSList *prefixes, dhcpv6_cb callback)
2649 {
2650         struct connman_service *service;
2651         struct connman_network *network;
2652         struct connman_dhcpv6 *dhcp;
2653
2654         if (index < 0)
2655                 return 0;
2656
2657         DBG("index %d", index);
2658
2659         service = __connman_service_lookup_from_index(index);
2660         if (!service)
2661                 return -EINVAL;
2662
2663         network = __connman_service_get_network(service);
2664         if (!network)
2665                 return -EINVAL;
2666
2667         if (network_pd_table) {
2668                 dhcp = g_hash_table_lookup(network_pd_table, network);
2669                 if (dhcp && dhcp->started)
2670                         return -EBUSY;
2671         }
2672
2673         dhcp = g_try_new0(struct connman_dhcpv6, 1);
2674         if (!dhcp)
2675                 return -ENOMEM;
2676
2677         dhcp->network = network;
2678         dhcp->callback = callback;
2679         dhcp->started = true;
2680
2681         if (!prefixes) {
2682                 /*
2683                  * Try to load the earlier prefixes if caller did not supply
2684                  * any that we could use.
2685                  */
2686                 struct connman_ipconfig *ipconfig;
2687                 ipconfig = __connman_service_get_ip6config(service);
2688
2689                 dhcp->prefixes = load_prefixes(ipconfig);
2690         } else
2691                 dhcp->prefixes = prefixes;
2692
2693         connman_network_ref(network);
2694
2695         DBG("replace network %p dhcp %p", network, dhcp);
2696
2697         g_hash_table_replace(network_pd_table, network, dhcp);
2698
2699         if (!dhcp->prefixes) {
2700                 /*
2701                  * Refresh start, try to get prefixes.
2702                  */
2703                 start_pd_solicitation(dhcp);
2704         } else {
2705                 /*
2706                  * We used to have prefixes, try to use them again.
2707                  * We need to use timeouts from confirm msg, RFC 3633, ch 12.1
2708                  */
2709                 start_pd_rebind_with_confirm(dhcp);
2710         }
2711
2712         return 0;
2713 }
2714
2715 void __connman_dhcpv6_stop_pd(int index)
2716 {
2717         struct connman_service *service;
2718         struct connman_network *network;
2719
2720         if (index < 0)
2721                 return;
2722
2723         DBG("index %d", index);
2724
2725         if (!network_pd_table)
2726                 return;
2727
2728         service = __connman_service_lookup_from_index(index);
2729         if (!service)
2730                 return;
2731
2732         network = __connman_service_get_network(service);
2733         if (!network)
2734                 return;
2735
2736         __connman_dhcpv6_start_pd_release(network, NULL);
2737
2738         if (g_hash_table_remove(network_pd_table, network))
2739                 connman_network_unref(network);
2740 }
2741
2742 int __connman_dhcpv6_init(void)
2743 {
2744         DBG("");
2745
2746         network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2747                                                         NULL, remove_network);
2748
2749         network_pd_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2750                                                         NULL, remove_network);
2751
2752         return 0;
2753 }
2754
2755 void __connman_dhcpv6_cleanup(void)
2756 {
2757         DBG("");
2758
2759         g_hash_table_destroy(network_table);
2760         network_table = NULL;
2761
2762         g_hash_table_destroy(network_pd_table);
2763         network_pd_table = NULL;
2764 }