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