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