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