Merge "[connman]Remove org.freedesktop.hostname1 service from Tizen platform." into...
[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                 __connman_ipconfig_address_remove(ipconfig);
689                 __connman_ipconfig_set_local(ipconfig, address);
690                 __connman_ipconfig_set_prefixlen(ipconfig, prefix_len);
691
692                 DBG("new address %s/%d", address, prefix_len);
693
694                 __connman_ipconfig_set_dhcp_address(ipconfig, address);
695                 __connman_service_save(
696                         __connman_service_lookup_from_index(ifindex));
697         }
698 }
699
700
701 /*
702  * Helper struct that is used when waiting a reply to DECLINE message.
703  */
704 struct decline_cb_data {
705         GDHCPClient *dhcp_client;
706         dhcpv6_cb callback;
707         int ifindex;
708         guint timeout;
709 };
710
711 static void decline_reply_callback(struct decline_cb_data *data)
712 {
713         struct connman_network *network;
714         struct connman_service *service;
715
716         service = __connman_service_lookup_from_index(data->ifindex);
717         network = __connman_service_get_network(service);
718
719         if (data->callback)
720                 data->callback(network, CONNMAN_DHCPV6_STATUS_RESTART, NULL);
721
722         g_dhcp_client_unref(data->dhcp_client);
723 }
724
725 static gboolean decline_timeout(gpointer user_data)
726 {
727         struct decline_cb_data *data = user_data;
728
729         DBG("ifindex %d", data->ifindex);
730
731         /*
732          * We ignore all DECLINE replies that are received after the timeout
733          */
734         g_dhcp_client_register_event(data->dhcp_client,
735                                         G_DHCP_CLIENT_EVENT_DECLINE,
736                                         NULL, NULL);
737
738         decline_reply_callback(data);
739
740         g_free(data);
741
742         return FALSE;
743 }
744
745 static void decline_cb(GDHCPClient *dhcp_client, gpointer user_data)
746 {
747         struct decline_cb_data *data = user_data;
748
749         DBG("ifindex %d", data->ifindex);
750
751         g_dhcpv6_client_clear_retransmit(dhcp_client);
752
753         if (data->timeout)
754                 g_source_remove(data->timeout);
755
756         decline_reply_callback(data);
757
758         g_free(data);
759 }
760
761 static int dhcpv6_decline(GDHCPClient *dhcp_client, int ifindex,
762                         dhcpv6_cb callback, GSList *failed)
763 {
764         struct decline_cb_data *data;
765         GList *option;
766         int code;
767
768         DBG("dhcp_client %p", dhcp_client);
769
770         g_dhcp_client_clear_requests(dhcp_client);
771
772         g_dhcpv6_client_clear_send(dhcp_client, G_DHCPV6_ORO);
773
774         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
775         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
776
777         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
778         if (!option) {
779                 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
780                 if (option)
781                         code = G_DHCPV6_IA_TA;
782                 else
783                         return -EINVAL;
784         } else
785                 code = G_DHCPV6_IA_NA;
786
787         g_dhcpv6_client_clear_send(dhcp_client, code);
788
789         g_dhcpv6_client_set_ias(dhcp_client, ifindex, code, NULL, NULL,
790                                 failed);
791
792         clear_callbacks(dhcp_client);
793
794         data = g_try_new(struct decline_cb_data, 1);
795         if (!data)
796                 return -ENOMEM;
797
798         data->ifindex = ifindex;
799         data->callback = callback;
800         data->dhcp_client = g_dhcp_client_ref(dhcp_client);
801         data->timeout = g_timeout_add(DEC_TIMEOUT, decline_timeout, data);
802
803         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_DECLINE,
804                                 decline_cb, data);
805
806         return g_dhcp_client_start(dhcp_client, NULL);
807 }
808
809 static void dad_reply(struct nd_neighbor_advert *reply,
810                 unsigned int length, struct in6_addr *addr, void *user_data)
811 {
812         struct own_address *data = user_data;
813         GSList *list;
814         char address[INET6_ADDRSTRLEN];
815         enum __connman_dhcpv6_status status = CONNMAN_DHCPV6_STATUS_FAIL;
816
817         inet_ntop(AF_INET6, addr, address, INET6_ADDRSTRLEN);
818
819         DBG("user %p reply %p len %d address %s index %d data %p", user_data,
820                 reply, length, address, data->ifindex, data);
821
822         if (!reply) {
823                 if (length == 0)
824                         DBG("DAD succeed for %s", address);
825                 else
826                         DBG("DAD cannot be done for %s", address);
827
828                 data->dad_succeed = g_slist_prepend(data->dad_succeed,
829                                                 g_strdup(address));
830
831         } else {
832                 DBG("DAD failed for %s", address);
833
834                 data->dad_failed = g_slist_prepend(data->dad_failed,
835                                                 g_strdup(address));
836         }
837
838         /*
839          * If refcount == 1 then we got the final reply and can continue.
840          */
841         if (data->refcount > 1)
842                 return;
843
844         for (list = data->dad_succeed; list; list = list->next)
845                 set_address(data->ifindex, data->ipconfig, data->prefixes,
846                                                                 list->data);
847
848         if (data->dad_failed) {
849                 dhcpv6_decline(data->dhcp_client, data->ifindex,
850                         data->callback, data->dad_failed);
851         } else {
852                 if (data->dad_succeed)
853                         status = CONNMAN_DHCPV6_STATUS_SUCCEED;
854
855                 if (data->callback) {
856                         struct connman_network *network;
857                         struct connman_service *service;
858
859                         service = __connman_service_lookup_from_index(
860                                                                 data->ifindex);
861                         network = __connman_service_get_network(service);
862                         if (network)
863                                 data->callback(network, status, NULL);
864                 }
865         }
866
867         unref_own_address(data);
868 }
869
870 /*
871  * Is the kernel configured to do DAD? If 0, then do not do DAD.
872  * See also RFC 4862 chapter 5.4 about DupAddrDetectTransmits
873  */
874 static int dad_transmits(int ifindex)
875 {
876         char name[IF_NAMESIZE];
877         gchar *path;
878         int value = 1;
879         FILE *f;
880
881         if (!if_indextoname(ifindex, name))
882                 return value;
883
884         path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/dad_transmits",
885                                                                 name);
886
887         if (!path)
888                 return value;
889
890         f = fopen(path, "r");
891
892         g_free(path);
893
894         if (f) {
895                 if (fscanf(f, "%d", &value) < 0)
896                         value = 1;
897
898                 fclose(f);
899         }
900
901         return value;
902 }
903
904 static void do_dad(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
905 {
906         struct connman_service *service;
907         struct connman_ipconfig *ipconfig;
908         int ifindex;
909         GList *option, *list;
910         struct own_address *user_data;
911
912         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
913         if (!option)
914                 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
915
916         /*
917          * Even if we didn't had any addresses, just try to set
918          * the other options.
919          */
920         set_other_addresses(dhcp_client, dhcp);
921
922         if (!option) {
923                 DBG("Skip DAD as no addresses found in reply");
924                 if (dhcp->callback)
925                         dhcp->callback(dhcp->network,
926                                         CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
927
928                 return;
929         }
930
931         ifindex = connman_network_get_index(dhcp->network);
932
933         DBG("index %d", ifindex);
934
935         service = connman_service_lookup_from_network(dhcp->network);
936         if (!service) {
937                 connman_error("Can not lookup service for index %d", ifindex);
938                 goto error;
939         }
940
941         ipconfig = __connman_service_get_ip6config(service);
942         if (!ipconfig) {
943                 connman_error("Could not lookup ip6config for index %d",
944                                                                 ifindex);
945                 goto error;
946         }
947
948         if (!dad_transmits(ifindex)) {
949                 DBG("Skip DAD because of kernel configuration");
950
951                 for (list = option; list; list = list->next)
952                         set_address(ifindex, ipconfig, dhcp->prefixes,
953                                                         option->data);
954
955                 if (dhcp->callback)
956                         dhcp->callback(dhcp->network,
957                                         CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
958
959                 return;
960         }
961
962         if (g_list_length(option) == 0) {
963                 DBG("No addresses when doing DAD");
964                 if (dhcp->callback)
965                         dhcp->callback(dhcp->network,
966                                         CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
967
968                 return;
969         }
970
971         user_data = g_try_new0(struct own_address, 1);
972         if (!user_data)
973                 goto error;
974
975         user_data->refcount = 0;
976         user_data->ifindex = ifindex;
977         user_data->dhcp_client = g_dhcp_client_ref(dhcp_client);
978         user_data->ipconfig = __connman_ipconfig_ref(ipconfig);
979         user_data->prefixes = copy_prefixes(dhcp->prefixes);
980         user_data->callback = dhcp->callback;
981
982         /*
983          * We send one neighbor discovery request / address
984          * and after all checks are done, then report the status
985          * via dhcp callback.
986          */
987
988         for (list = option; list; list = list->next) {
989                 char *address = option->data;
990                 struct in6_addr addr;
991                 int ret;
992
993                 ref_own_address(user_data);
994
995                 if (inet_pton(AF_INET6, address, &addr) < 0) {
996                         DBG("Invalid IPv6 address %s %d/%s", address,
997                                 -errno, strerror(errno));
998                         goto fail;
999                 }
1000
1001                 DBG("user %p address %s client %p ipconfig %p prefixes %p",
1002                         user_data, address,
1003                         user_data->dhcp_client, user_data->ipconfig,
1004                         user_data->prefixes);
1005
1006                 ret = __connman_inet_ipv6_do_dad(ifindex, 1000,
1007                                                 &addr,
1008                                                 dad_reply,
1009                                                 user_data);
1010                 if (ret < 0) {
1011                         DBG("Could not send neighbor solicitation for %s",
1012                                                                 address);
1013                         dad_reply(NULL, -1, &addr, user_data);
1014                 } else {
1015                         DBG("Sent neighbor solicitation %d bytes", ret);
1016                 }
1017         }
1018
1019         return;
1020
1021 fail:
1022         unref_own_address(user_data);
1023
1024 error:
1025         if (dhcp->callback)
1026                 dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
1027                                                                 NULL);
1028 }
1029
1030 static gboolean timeout_request_resend(gpointer user_data)
1031 {
1032         struct connman_dhcpv6 *dhcp = user_data;
1033
1034         if (dhcp->request_count >= REQ_MAX_RC) {
1035                 DBG("max request retry attempts %d", dhcp->request_count);
1036                 dhcp->request_count = 0;
1037                 if (dhcp->callback)
1038                         dhcp->callback(dhcp->network,
1039                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1040                 return FALSE;
1041         }
1042
1043         dhcp->request_count++;
1044
1045         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1046         DBG("request resend RT timeout %d msec", dhcp->RT);
1047         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request_resend, dhcp);
1048
1049         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1050
1051         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1052
1053         return FALSE;
1054 }
1055
1056 static gboolean request_resend(gpointer user_data)
1057 {
1058         struct connman_dhcpv6 *dhcp = user_data;
1059
1060         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1061
1062         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1063         DBG("request resend RT timeout %d msec", dhcp->RT);
1064         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request_resend, dhcp);
1065
1066         dhcpv6_request(dhcp, true);
1067
1068         return FALSE;
1069 }
1070
1071 static void do_resend_request(struct connman_dhcpv6 *dhcp)
1072 {
1073         if (dhcp->request_count >= REQ_MAX_RC) {
1074                 DBG("max request retry attempts %d", dhcp->request_count);
1075                 dhcp->request_count = 0;
1076                 if (dhcp->callback)
1077                         dhcp->callback(dhcp->network,
1078                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1079                 return;
1080         }
1081
1082         dhcp->request_count++;
1083
1084         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1085         DBG("resending request after %d msec", dhcp->RT);
1086         dhcp->timeout = g_timeout_add(dhcp->RT, request_resend, dhcp);
1087 }
1088
1089 static void re_cb(enum request_type req_type, GDHCPClient *dhcp_client,
1090                 gpointer user_data)
1091 {
1092         struct connman_dhcpv6 *dhcp = user_data;
1093         uint16_t status;
1094
1095         clear_timer(dhcp);
1096
1097         status = g_dhcpv6_client_get_status(dhcp_client);
1098
1099         DBG("dhcpv6 cb msg %p status %d", dhcp, status);
1100
1101         /*
1102          * RFC 3315, 18.1.8 handle the resend if error
1103          */
1104         if (status  == G_DHCPV6_ERROR_BINDING) {
1105                 dhcpv6_request(dhcp, false);
1106         } else if (status  == G_DHCPV6_ERROR_MCAST) {
1107                 switch (req_type) {
1108                 case REQ_REQUEST:
1109                         dhcpv6_request(dhcp, true);
1110                         break;
1111                 case REQ_REBIND:
1112                         dhcpv6_rebind(dhcp);
1113                         break;
1114                 case REQ_RENEW:
1115                         dhcpv6_renew(dhcp);
1116                         break;
1117                 }
1118         } else if (status  == G_DHCPV6_ERROR_LINK) {
1119                 if (req_type == REQ_REQUEST) {
1120                         g_dhcp_client_unref(dhcp->dhcp_client);
1121                         start_solicitation(dhcp);
1122                 } else {
1123                         if (dhcp->callback)
1124                                 dhcp->callback(dhcp->network,
1125                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1126                 }
1127         } else if (status  == G_DHCPV6_ERROR_FAILURE) {
1128                 if (req_type == REQ_REQUEST) {
1129                         /* Rate limit the resend of request message */
1130                         do_resend_request(dhcp);
1131                 } else {
1132                         if (dhcp->callback)
1133                                 dhcp->callback(dhcp->network,
1134                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1135                 }
1136         } else {
1137
1138                 /*
1139                  * If we did not got any addresses, then re-send
1140                  * a request.
1141                  */
1142                 GList *option;
1143
1144                 option = g_dhcp_client_get_option(dhcp->dhcp_client,
1145                                                 G_DHCPV6_IA_NA);
1146                 if (!option) {
1147                         option = g_dhcp_client_get_option(dhcp->dhcp_client,
1148                                                         G_DHCPV6_IA_TA);
1149                         if (!option) {
1150                                 switch (req_type) {
1151                                 case REQ_REQUEST:
1152                                         do_resend_request(dhcp);
1153                                         break;
1154                                 case REQ_REBIND:
1155                                         dhcpv6_rebind(dhcp);
1156                                         break;
1157                                 case REQ_RENEW:
1158                                         dhcpv6_renew(dhcp);
1159                                         break;
1160                                 }
1161                                 return;
1162                         }
1163                 }
1164
1165                 if (status == G_DHCPV6_ERROR_SUCCESS)
1166                         do_dad(dhcp_client, dhcp);
1167                 else if (dhcp->callback)
1168                         dhcp->callback(dhcp->network,
1169                                 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1170         }
1171 }
1172
1173 static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data)
1174 {
1175         DBG("");
1176
1177         g_dhcpv6_client_reset_request(dhcp_client);
1178         g_dhcpv6_client_clear_retransmit(dhcp_client);
1179
1180         re_cb(REQ_REBIND, dhcp_client, user_data);
1181 }
1182
1183 static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp)
1184 {
1185         GDHCPClient *dhcp_client;
1186
1187         DBG("dhcp %p", dhcp);
1188
1189         dhcp_client = dhcp->dhcp_client;
1190
1191         g_dhcp_client_clear_requests(dhcp_client);
1192
1193         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1194         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1195         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1196         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1197
1198         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1199                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1200
1201         g_dhcpv6_client_set_ia(dhcp_client,
1202                         connman_network_get_index(dhcp->network),
1203                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1204                         NULL, NULL, TRUE, NULL);
1205
1206         clear_callbacks(dhcp_client);
1207
1208         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
1209                                         rebind_cb, dhcp);
1210
1211         dhcp->dhcp_client = dhcp_client;
1212
1213         return g_dhcp_client_start(dhcp_client, NULL);
1214 }
1215
1216 static gboolean dhcpv6_restart(gpointer user_data)
1217 {
1218         struct connman_dhcpv6 *dhcp = user_data;
1219
1220         if (dhcp->callback)
1221                 dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
1222                                                                 NULL);
1223
1224         return FALSE;
1225 }
1226
1227 /*
1228  * Check if we need to restart the solicitation procedure. This
1229  * is done if all the addresses have expired. RFC 3315, 18.1.4
1230  */
1231 static int check_restart(struct connman_dhcpv6 *dhcp)
1232 {
1233         time_t current, expired;
1234
1235         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
1236                                 NULL, &expired);
1237
1238         /* infinite lifetime for an DHCPv6 address */
1239         if (expired == 0xffffffff)
1240                 return -EISCONN;
1241
1242         current = time(NULL);
1243
1244         if (current >= expired) {
1245                 DBG("expired by %d secs", (int)(current - expired));
1246
1247                 g_idle_add(dhcpv6_restart, dhcp);
1248
1249                 return -ETIMEDOUT;
1250         }
1251
1252         return 0;
1253 }
1254
1255 static gboolean timeout_rebind(gpointer user_data)
1256 {
1257         struct connman_dhcpv6 *dhcp = user_data;
1258
1259         if (check_restart(dhcp) < 0)
1260                 return FALSE;
1261
1262         dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
1263
1264         DBG("rebind RT timeout %d msec", dhcp->RT);
1265
1266         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1267
1268         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1269
1270         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1271
1272         return FALSE;
1273 }
1274
1275 static gboolean start_rebind(gpointer user_data)
1276 {
1277         struct connman_dhcpv6 *dhcp = user_data;
1278
1279         if (check_restart(dhcp) < 0)
1280                 return FALSE;
1281
1282         dhcp->RT = initial_rt(REB_TIMEOUT);
1283
1284         DBG("rebind initial RT timeout %d msec", dhcp->RT);
1285
1286         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1287
1288         dhcpv6_rebind(dhcp);
1289
1290         return FALSE;
1291 }
1292
1293 static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
1294 {
1295         DBG("");
1296
1297         g_dhcpv6_client_reset_request(dhcp_client);
1298         g_dhcpv6_client_clear_retransmit(dhcp_client);
1299
1300         re_cb(REQ_REQUEST, dhcp_client, user_data);
1301 }
1302
1303 static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
1304                         bool add_addresses)
1305 {
1306         GDHCPClient *dhcp_client;
1307         uint32_t T1, T2;
1308
1309         DBG("dhcp %p add %d", dhcp, add_addresses);
1310
1311         dhcp_client = dhcp->dhcp_client;
1312
1313         g_dhcp_client_clear_requests(dhcp_client);
1314
1315         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1316         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1317         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1318         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1319         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1320
1321         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1322                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1323
1324         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1325         g_dhcpv6_client_set_ia(dhcp_client,
1326                         connman_network_get_index(dhcp->network),
1327                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1328                         &T1, &T2, add_addresses, NULL);
1329
1330         clear_callbacks(dhcp_client);
1331
1332         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
1333                                         request_cb, dhcp);
1334
1335         dhcp->dhcp_client = dhcp_client;
1336
1337         return g_dhcp_client_start(dhcp_client, NULL);
1338 }
1339
1340 static gboolean timeout_request(gpointer user_data)
1341 {
1342         struct connman_dhcpv6 *dhcp = user_data;
1343
1344         if (dhcp->request_count >= REQ_MAX_RC) {
1345                 DBG("max request retry attempts %d", dhcp->request_count);
1346                 dhcp->request_count = 0;
1347                 if (dhcp->callback)
1348                         dhcp->callback(dhcp->network,
1349                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1350                 return FALSE;
1351         }
1352
1353         dhcp->request_count++;
1354
1355         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1356         DBG("request RT timeout %d msec", dhcp->RT);
1357         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1358
1359         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1360
1361         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1362
1363         return FALSE;
1364 }
1365
1366 static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
1367 {
1368         DBG("");
1369
1370         g_dhcpv6_client_reset_request(dhcp_client);
1371         g_dhcpv6_client_clear_retransmit(dhcp_client);
1372
1373         re_cb(REQ_RENEW, dhcp_client, user_data);
1374 }
1375
1376 static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
1377 {
1378         GDHCPClient *dhcp_client;
1379         uint32_t T1, T2;
1380
1381         DBG("dhcp %p", dhcp);
1382
1383         dhcp_client = dhcp->dhcp_client;
1384
1385         g_dhcp_client_clear_requests(dhcp_client);
1386
1387         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1388         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1389         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1390         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1391         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1392
1393         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1394                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1395
1396         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1397         g_dhcpv6_client_set_ia(dhcp_client,
1398                         connman_network_get_index(dhcp->network),
1399                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1400                         &T1, &T2, TRUE, NULL);
1401
1402         clear_callbacks(dhcp_client);
1403
1404         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
1405                                         renew_cb, dhcp);
1406
1407         dhcp->dhcp_client = dhcp_client;
1408
1409         return g_dhcp_client_start(dhcp_client, NULL);
1410 }
1411
1412 static gboolean timeout_renew(gpointer user_data)
1413 {
1414         struct connman_dhcpv6 *dhcp = user_data;
1415         time_t last_rebind, current;
1416         uint32_t T2;
1417
1418         if (check_restart(dhcp) < 0)
1419                 return FALSE;
1420
1421         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, &T2,
1422                                 &last_rebind, NULL);
1423         current = time(NULL);
1424         if ((unsigned)current >= (unsigned)last_rebind + T2) {
1425                 /*
1426                  * Do rebind instead if past T2
1427                  */
1428                 start_rebind(dhcp);
1429                 return FALSE;
1430         }
1431
1432         dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
1433
1434         DBG("renew RT timeout %d msec", dhcp->RT);
1435
1436         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1437
1438         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1439
1440         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1441
1442         return FALSE;
1443 }
1444
1445 static gboolean start_renew(gpointer user_data)
1446 {
1447         struct connman_dhcpv6 *dhcp = user_data;
1448
1449         dhcp->RT = initial_rt(REN_TIMEOUT);
1450
1451         DBG("renew initial RT timeout %d msec", dhcp->RT);
1452
1453         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1454
1455         dhcpv6_renew(dhcp);
1456
1457         return FALSE;
1458 }
1459
1460 int __connman_dhcpv6_start_renew(struct connman_network *network,
1461                                                         dhcpv6_cb callback)
1462 {
1463         struct connman_dhcpv6 *dhcp;
1464         uint32_t T1, T2, delta;
1465         time_t started, current, expired;
1466
1467         dhcp = g_hash_table_lookup(network_table, network);
1468         if (!dhcp)
1469                 return -ENOENT;
1470
1471         DBG("network %p dhcp %p", network, dhcp);
1472
1473         clear_timer(dhcp);
1474
1475         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
1476                                 &started, &expired);
1477
1478         current = time(NULL);
1479
1480         DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
1481                 (unsigned long)expired, current, started);
1482
1483         if (T1 == 0xffffffff)
1484                 /* RFC 3315, 22.4 */
1485                 return 0;
1486
1487         if (T1 == 0) {
1488                 /* RFC 3315, 22.4
1489                  * Client can choose the timeout.
1490                  */
1491                 T1 = (expired - started) / 2;
1492                 T2 = (expired - started) / 10 * 8;
1493         }
1494
1495         dhcp->callback = callback;
1496
1497         /* RFC 3315, 18.1.4, start solicit if expired */
1498         if (check_restart(dhcp) < 0)
1499                 return 0;
1500
1501         if (T2 != 0xffffffff && T2 > 0) {
1502                 if ((unsigned)current >= (unsigned)started + T2) {
1503                         /* RFC 3315, chapter 18.1.3, start rebind */
1504                         DBG("start rebind immediately");
1505
1506                         dhcp->timeout = g_idle_add(start_rebind, dhcp);
1507
1508                 } else if ((unsigned)current < (unsigned)started + T1) {
1509                         delta = started + T1 - current;
1510                         DBG("renew after %d secs", delta);
1511
1512                         dhcp->timeout = g_timeout_add_seconds(delta,
1513                                         start_renew, dhcp);
1514                 } else {
1515                         delta = started + T2 - current;
1516                         DBG("rebind after %d secs", delta);
1517
1518                         dhcp->timeout = g_timeout_add_seconds(delta,
1519                                         start_rebind, dhcp);
1520                 }
1521         }
1522
1523         return 0;
1524 }
1525
1526 static void release_cb(GDHCPClient *dhcp_client, gpointer user_data)
1527 {
1528         DBG("");
1529 }
1530
1531 int __connman_dhcpv6_start_release(struct connman_network *network,
1532                                 dhcpv6_cb callback)
1533 {
1534         struct connman_dhcpv6 *dhcp;
1535         GDHCPClient *dhcp_client;
1536
1537         if (!network_table)
1538                 return 0;   /* we are already released */
1539
1540         dhcp = g_hash_table_lookup(network_table, network);
1541         if (!dhcp)
1542                 return -ENOENT;
1543
1544         DBG("network %p dhcp %p client %p stateless %d", network, dhcp,
1545                                         dhcp->dhcp_client, dhcp->stateless);
1546
1547         if (dhcp->stateless)
1548                 return -EINVAL;
1549
1550         clear_timer(dhcp);
1551
1552         dhcp_client = dhcp->dhcp_client;
1553         if (!dhcp_client) {
1554                 /*
1555                  * We had started the DHCPv6 handshaking i.e., we have called
1556                  * __connman_dhcpv6_start() but it has not yet sent
1557                  * a solicitation message to server. This means that we do not
1558                  * have DHCPv6 configured yet so we can just quit here.
1559                  */
1560                 DBG("DHCPv6 was not started");
1561                 return 0;
1562         }
1563
1564         g_dhcp_client_clear_requests(dhcp_client);
1565         g_dhcp_client_clear_values(dhcp_client);
1566
1567         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1568         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1569
1570         g_dhcpv6_client_set_ia(dhcp_client,
1571                         connman_network_get_index(dhcp->network),
1572                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1573                         NULL, NULL, TRUE, NULL);
1574
1575         clear_callbacks(dhcp_client);
1576
1577         /*
1578          * Although we register a callback here we are really not interested in
1579          * the answer because it might take too long time and network code
1580          * might be in the middle of the disconnect.
1581          * So we just inform the server that we are done with the addresses
1582          * but ignore the reply from server. This is allowed by RFC 3315
1583          * chapter 18.1.6.
1584          */
1585         g_dhcp_client_register_event(dhcp_client,
1586                                 G_DHCP_CLIENT_EVENT_RELEASE,
1587                                 release_cb, NULL);
1588
1589         dhcp->dhcp_client = dhcp_client;
1590
1591         return g_dhcp_client_start(dhcp_client, NULL);
1592 }
1593
1594 static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
1595 {
1596         DBG("dhcp %p", dhcp);
1597
1598         clear_timer(dhcp);
1599
1600         dhcpv6_free(dhcp);
1601
1602         if (!dhcp->dhcp_client)
1603                 return 0;
1604
1605         g_dhcp_client_stop(dhcp->dhcp_client);
1606         g_dhcp_client_unref(dhcp->dhcp_client);
1607
1608         dhcp->dhcp_client = NULL;
1609
1610         return 0;
1611 }
1612
1613 static void remove_network(gpointer user_data)
1614 {
1615         struct connman_dhcpv6 *dhcp = user_data;
1616
1617         DBG("dhcp %p", dhcp);
1618
1619         dhcpv6_release(dhcp);
1620
1621         g_free(dhcp);
1622 }
1623
1624 static gboolean timeout_info_req(gpointer user_data)
1625 {
1626         struct connman_dhcpv6 *dhcp = user_data;
1627
1628         dhcp->RT = calc_delay(dhcp->RT, INF_MAX_RT);
1629
1630         DBG("info RT timeout %d msec", dhcp->RT);
1631
1632         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1633
1634         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1635
1636         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1637
1638         return FALSE;
1639 }
1640
1641 static gboolean start_info_req(gpointer user_data)
1642 {
1643         struct connman_dhcpv6 *dhcp = user_data;
1644
1645         /* Set the retransmission timeout, RFC 3315 chapter 14 */
1646         dhcp->RT = initial_rt(INF_TIMEOUT);
1647
1648         DBG("info initial RT timeout %d msec", dhcp->RT);
1649
1650         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1651
1652         dhcpv6_info_request(dhcp);
1653
1654         return FALSE;
1655 }
1656
1657 int __connman_dhcpv6_start_info(struct connman_network *network,
1658                                 dhcpv6_cb callback)
1659 {
1660         struct connman_dhcpv6 *dhcp;
1661         int delay;
1662         uint64_t rand;
1663
1664         DBG("");
1665
1666         if (network_table) {
1667                 dhcp = g_hash_table_lookup(network_table, network);
1668                 if (dhcp && dhcp->started)
1669                         return -EBUSY;
1670         }
1671
1672         dhcp = g_try_new0(struct connman_dhcpv6, 1);
1673         if (!dhcp)
1674                 return -ENOMEM;
1675
1676         dhcp->network = network;
1677         dhcp->callback = callback;
1678         dhcp->stateless = true;
1679         dhcp->started = true;
1680
1681         connman_network_ref(network);
1682
1683         DBG("replace network %p dhcp %p", network, dhcp);
1684
1685         g_hash_table_replace(network_table, network, dhcp);
1686
1687         /* Initial timeout, RFC 3315, 18.1.5 */
1688         __connman_util_get_random(&rand);
1689         delay = rand % 1000;
1690
1691         dhcp->timeout = g_timeout_add(delay, start_info_req, dhcp);
1692
1693         return 0;
1694 }
1695
1696 static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data)
1697 {
1698         struct connman_dhcpv6 *dhcp = user_data;
1699
1700         DBG("dhcpv6 advertise msg %p", dhcp);
1701
1702         clear_timer(dhcp);
1703
1704         g_dhcpv6_client_clear_retransmit(dhcp_client);
1705
1706         if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1707                 if (dhcp->callback)
1708                         dhcp->callback(dhcp->network,
1709                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1710                 return;
1711         }
1712
1713         dhcp->RT = initial_rt(REQ_TIMEOUT);
1714         DBG("request initial RT timeout %d msec", dhcp->RT);
1715         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1716
1717         dhcp->request_count = 1;
1718
1719         dhcpv6_request(dhcp, true);
1720 }
1721
1722 static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)
1723 {
1724         /* We get here if server supports rapid commit */
1725         struct connman_dhcpv6 *dhcp = user_data;
1726
1727         DBG("dhcpv6 solicitation msg %p", dhcp);
1728
1729         clear_timer(dhcp);
1730
1731         g_dhcpv6_client_clear_retransmit(dhcp_client);
1732
1733         if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1734                 if (dhcp->callback)
1735                         dhcp->callback(dhcp->network,
1736                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1737                 return;
1738         }
1739
1740         do_dad(dhcp_client, dhcp);
1741 }
1742
1743 static gboolean timeout_solicitation(gpointer user_data)
1744 {
1745         struct connman_dhcpv6 *dhcp = user_data;
1746
1747         dhcp->RT = calc_delay(dhcp->RT, SOL_MAX_RT);
1748
1749         DBG("solicit RT timeout %d msec", dhcp->RT);
1750
1751         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1752
1753         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1754
1755         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1756
1757         return FALSE;
1758 }
1759
1760 static int dhcpv6_solicitation(struct connman_dhcpv6 *dhcp)
1761 {
1762         struct connman_service *service;
1763         struct connman_ipconfig *ipconfig_ipv6;
1764         GDHCPClient *dhcp_client;
1765         GDHCPClientError error;
1766         int index, ret;
1767
1768         DBG("dhcp %p", dhcp);
1769
1770         index = connman_network_get_index(dhcp->network);
1771
1772         dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
1773         if (error != G_DHCP_CLIENT_ERROR_NONE) {
1774                 clear_timer(dhcp);
1775                 return -EINVAL;
1776         }
1777
1778 #if !defined TIZEN_EXT
1779         if (getenv("CONNMAN_DHCPV6_DEBUG"))
1780 #endif
1781                 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
1782
1783         service = connman_service_lookup_from_network(dhcp->network);
1784         if (!service) {
1785                 clear_timer(dhcp);
1786                 g_dhcp_client_unref(dhcp_client);
1787                 return -EINVAL;
1788         }
1789
1790         ret = set_duid(service, dhcp->network, dhcp_client, index);
1791         if (ret < 0) {
1792                 clear_timer(dhcp);
1793                 g_dhcp_client_unref(dhcp_client);
1794                 return ret;
1795         }
1796
1797         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1798         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
1799         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1800         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1801         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1802
1803         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1804                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1805
1806         ipconfig_ipv6 = __connman_service_get_ip6config(service);
1807         dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
1808
1809         g_dhcpv6_client_set_ia(dhcp_client, index,
1810                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1811                         NULL, NULL, FALSE, NULL);
1812
1813         clear_callbacks(dhcp_client);
1814
1815         g_dhcp_client_register_event(dhcp_client,
1816                                 G_DHCP_CLIENT_EVENT_SOLICITATION,
1817                                 solicitation_cb, dhcp);
1818
1819         g_dhcp_client_register_event(dhcp_client,
1820                                 G_DHCP_CLIENT_EVENT_ADVERTISE,
1821                                 advertise_cb, dhcp);
1822
1823         dhcp->dhcp_client = dhcp_client;
1824
1825         return g_dhcp_client_start(dhcp_client, NULL);
1826 }
1827
1828 static gboolean start_solicitation(gpointer user_data)
1829 {
1830         struct connman_dhcpv6 *dhcp = user_data;
1831
1832         /* Set the retransmission timeout, RFC 3315 chapter 14 */
1833         dhcp->RT = initial_rt(SOL_TIMEOUT);
1834
1835         DBG("solicit initial RT timeout %d msec", dhcp->RT);
1836
1837         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1838
1839         dhcpv6_solicitation(dhcp);
1840
1841         return FALSE;
1842 }
1843
1844 int __connman_dhcpv6_start(struct connman_network *network,
1845                                 GSList *prefixes, dhcpv6_cb callback)
1846 {
1847         struct connman_service *service;
1848         struct connman_dhcpv6 *dhcp;
1849         int delay;
1850         uint64_t rand;
1851
1852         DBG("");
1853
1854         if (network_table) {
1855                 dhcp = g_hash_table_lookup(network_table, network);
1856                 if (dhcp && dhcp->started)
1857                         return -EBUSY;
1858         }
1859
1860         service = connman_service_lookup_from_network(network);
1861         if (!service)
1862                 return -EINVAL;
1863
1864         dhcp = g_try_new0(struct connman_dhcpv6, 1);
1865         if (!dhcp)
1866                 return -ENOMEM;
1867
1868         dhcp->network = network;
1869         dhcp->callback = callback;
1870         dhcp->prefixes = prefixes;
1871         dhcp->started = true;
1872
1873         connman_network_ref(network);
1874
1875         DBG("replace network %p dhcp %p", network, dhcp);
1876
1877         g_hash_table_replace(network_table, network, dhcp);
1878
1879         /* Initial timeout, RFC 3315, 17.1.2 */
1880         __connman_util_get_random(&rand);
1881         delay = rand % 1000;
1882
1883         /*
1884          * Start from scratch.
1885          * RFC 3315, chapter 17.1.2 Solicitation message
1886          *
1887          * Note that we do not send CONFIRM message here as it does
1888          * not make much sense because we do not save expiration time
1889          * so we cannot really know how long the saved address is valid
1890          * anyway. The reply to CONFIRM message does not send
1891          * expiration times back to us. Because of this we need to
1892          * start using SOLICITATION anyway.
1893          */
1894         dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
1895
1896         return 0;
1897 }
1898
1899 void __connman_dhcpv6_stop(struct connman_network *network)
1900 {
1901         DBG("");
1902
1903         if (!network_table)
1904                 return;
1905
1906         if (g_hash_table_remove(network_table, network))
1907                 connman_network_unref(network);
1908 }
1909
1910 static int save_prefixes(struct connman_ipconfig *ipconfig,
1911                         GSList *prefixes)
1912 {
1913         GSList *list;
1914         int i = 0, count = g_slist_length(prefixes);
1915         char **array;
1916
1917         if (count == 0)
1918                 return 0;
1919
1920         array = g_try_new0(char *, count + 1);
1921         if (!array)
1922                 return -ENOMEM;
1923
1924         for (list = prefixes; list; list = list->next) {
1925                 char *elem, addr_str[INET6_ADDRSTRLEN];
1926                 GDHCPIAPrefix *prefix = list->data;
1927
1928                 elem = g_strdup_printf("%s/%d", inet_ntop(AF_INET6,
1929                                 &prefix->prefix, addr_str, INET6_ADDRSTRLEN),
1930                                 prefix->prefixlen);
1931                 if (!elem) {
1932                         g_strfreev(array);
1933                         return -ENOMEM;
1934                 }
1935
1936                 array[i++] = elem;
1937         }
1938
1939         __connman_ipconfig_set_dhcpv6_prefixes(ipconfig, array);
1940         return 0;
1941 }
1942
1943 static GSList *load_prefixes(struct connman_ipconfig *ipconfig)
1944 {
1945         int i;
1946         GSList *list = NULL;
1947         char **array =  __connman_ipconfig_get_dhcpv6_prefixes(ipconfig);
1948
1949         if (!array)
1950                 return NULL;
1951
1952         for (i = 0; array[i]; i++) {
1953                 GDHCPIAPrefix *prefix;
1954                 long int value;
1955                 char *ptr, **elems = g_strsplit(array[i], "/", 0);
1956
1957                 if (!elems)
1958                         return list;
1959
1960                 value = strtol(elems[1], &ptr, 10);
1961                 if (ptr != elems[1] && *ptr == '\0' && value <= 128) {
1962                         struct in6_addr addr;
1963
1964                         if (inet_pton(AF_INET6, elems[0], &addr) == 1) {
1965                                 prefix = g_try_new0(GDHCPIAPrefix, 1);
1966                                 if (!prefix) {
1967                                         g_strfreev(elems);
1968                                         return list;
1969                                 }
1970                                 memcpy(&prefix->prefix, &addr,
1971                                         sizeof(struct in6_addr));
1972                                 prefix->prefixlen = value;
1973
1974                                 list = g_slist_prepend(list, prefix);
1975                         }
1976                 }
1977
1978                 g_strfreev(elems);
1979         }
1980
1981         return list;
1982 }
1983
1984 static GDHCPIAPrefix *copy_prefix(gpointer data)
1985 {
1986         GDHCPIAPrefix *copy, *prefix = data;
1987
1988         copy = g_try_new(GDHCPIAPrefix, 1);
1989         if (!copy)
1990                 return NULL;
1991
1992         memcpy(copy, prefix, sizeof(GDHCPIAPrefix));
1993
1994         return copy;
1995 }
1996
1997 static GSList *copy_and_convert_prefixes(GList *prefixes)
1998 {
1999         GSList *copy = NULL;
2000         GList *list;
2001
2002         for (list = prefixes; list; list = list->next)
2003                 copy = g_slist_prepend(copy, copy_prefix(list->data));
2004
2005         return copy;
2006 }
2007
2008 static int set_prefixes(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
2009 {
2010         if (dhcp->prefixes)
2011                 g_slist_free_full(dhcp->prefixes, free_prefix);
2012
2013         dhcp->prefixes =
2014                 copy_and_convert_prefixes(g_dhcp_client_get_option(dhcp_client,
2015                                                         G_DHCPV6_IA_PD));
2016
2017         DBG("Got %d prefix", g_slist_length(dhcp->prefixes));
2018
2019         if (dhcp->callback) {
2020                 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
2021                 if (status == G_DHCPV6_ERROR_NO_PREFIX)
2022                         dhcp->callback(dhcp->network,
2023                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2024                 else {
2025                         struct connman_service *service;
2026                         struct connman_ipconfig *ipconfig;
2027                         int ifindex = connman_network_get_index(dhcp->network);
2028
2029                         service = __connman_service_lookup_from_index(ifindex);
2030                         if (service) {
2031                                 ipconfig = __connman_service_get_ip6config(
2032                                                                 service);
2033                                 save_prefixes(ipconfig, dhcp->prefixes);
2034                                 __connman_service_save(service);
2035                         }
2036
2037                         dhcp->callback(dhcp->network,
2038                                 CONNMAN_DHCPV6_STATUS_SUCCEED, dhcp->prefixes);
2039                 }
2040         } else {
2041                 g_slist_free_full(dhcp->prefixes, free_prefix);
2042                 dhcp->prefixes = NULL;
2043         }
2044
2045         return 0;
2046 }
2047
2048 static void re_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2049 {
2050         struct connman_dhcpv6 *dhcp = user_data;
2051         uint16_t status;
2052         int ret;
2053
2054         status = g_dhcpv6_client_get_status(dhcp_client);
2055
2056         DBG("dhcpv6 cb msg %p status %d", dhcp, status);
2057
2058         if (status  == G_DHCPV6_ERROR_BINDING) {
2059                 /* RFC 3315, 18.1.8 */
2060                 dhcpv6_pd_request(dhcp);
2061         } else {
2062                 ret = set_prefixes(dhcp_client, dhcp);
2063                 if (ret < 0) {
2064                         if (dhcp->callback)
2065                                 dhcp->callback(dhcp->network,
2066                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2067                         return;
2068                 }
2069         }
2070 }
2071
2072 static void rebind_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2073 {
2074         DBG("");
2075
2076         g_dhcpv6_client_clear_retransmit(dhcp_client);
2077
2078         re_pd_cb(dhcp_client, user_data);
2079 }
2080
2081 static GDHCPClient *create_pd_client(struct connman_dhcpv6 *dhcp, int *err)
2082 {
2083         GDHCPClient *dhcp_client;
2084         GDHCPClientError error;
2085         struct connman_service *service;
2086         int index, ret;
2087         uint32_t iaid;
2088
2089         index = connman_network_get_index(dhcp->network);
2090
2091         dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
2092         if (error != G_DHCP_CLIENT_ERROR_NONE) {
2093                 *err = -EINVAL;
2094                 return NULL;
2095         }
2096
2097 #if !defined TIZEN_EXT
2098         if (getenv("CONNMAN_DHCPV6_DEBUG"))
2099 #endif
2100                 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6:PD");
2101
2102         service = connman_service_lookup_from_network(dhcp->network);
2103         if (!service) {
2104                 g_dhcp_client_unref(dhcp_client);
2105                 *err = -EINVAL;
2106                 return NULL;
2107         }
2108
2109         ret = set_duid(service, dhcp->network, dhcp_client, index);
2110         if (ret < 0) {
2111                 g_dhcp_client_unref(dhcp_client);
2112                 *err = ret;
2113                 return NULL;
2114         }
2115
2116         g_dhcpv6_client_create_iaid(dhcp_client, index, (unsigned char *)&iaid);
2117         g_dhcpv6_client_set_iaid(dhcp_client, iaid);
2118
2119         return dhcp_client;
2120 }
2121
2122 static int dhcpv6_pd_rebind(struct connman_dhcpv6 *dhcp)
2123 {
2124         GDHCPClient *dhcp_client;
2125         uint32_t T1, T2;
2126
2127         DBG("dhcp %p", dhcp);
2128
2129         if (!dhcp->dhcp_client) {
2130                 /*
2131                  * We skipped the solicitation phase
2132                  */
2133                 int err;
2134
2135                 dhcp->dhcp_client = create_pd_client(dhcp, &err);
2136                 if (!dhcp->dhcp_client) {
2137                         clear_timer(dhcp);
2138                         return err;
2139                 }
2140         }
2141
2142         dhcp_client = dhcp->dhcp_client;
2143
2144         g_dhcp_client_clear_requests(dhcp_client);
2145
2146         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2147         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2148         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2149
2150         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2151         g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2152
2153         clear_callbacks(dhcp_client);
2154
2155         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
2156                                         rebind_pd_cb, dhcp);
2157
2158         return g_dhcp_client_start(dhcp_client, NULL);
2159 }
2160
2161 static void renew_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2162 {
2163         DBG("");
2164
2165         g_dhcpv6_client_clear_retransmit(dhcp_client);
2166
2167         re_pd_cb(dhcp_client, user_data);
2168 }
2169
2170 static int dhcpv6_pd_renew(struct connman_dhcpv6 *dhcp)
2171 {
2172         GDHCPClient *dhcp_client;
2173         uint32_t T1, T2;
2174
2175         DBG("dhcp %p", dhcp);
2176
2177         dhcp_client = dhcp->dhcp_client;
2178
2179         g_dhcp_client_clear_requests(dhcp_client);
2180
2181         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2182         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2183         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2184         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2185
2186         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2187         g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2188
2189         clear_callbacks(dhcp_client);
2190
2191         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
2192                                         renew_pd_cb, dhcp);
2193
2194         return g_dhcp_client_start(dhcp_client, NULL);
2195 }
2196
2197 /*
2198  * Check if we need to restart the solicitation procedure. This
2199  * is done if all the prefixes have expired.
2200  */
2201 static int check_pd_restart(struct connman_dhcpv6 *dhcp)
2202 {
2203         time_t current, expired;
2204
2205         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
2206                                 NULL, &expired);
2207         current = time(NULL);
2208
2209         if (current > expired) {
2210                 DBG("expired by %d secs", (int)(current - expired));
2211
2212                 g_idle_add(dhcpv6_restart, dhcp);
2213
2214                 return -ETIMEDOUT;
2215         }
2216
2217         return 0;
2218 }
2219
2220 static gboolean timeout_pd_rebind(gpointer user_data)
2221 {
2222         struct connman_dhcpv6 *dhcp = user_data;
2223
2224         if (check_pd_restart(dhcp) < 0)
2225                 return FALSE;
2226
2227         dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
2228
2229         DBG("rebind RT timeout %d msec", dhcp->RT);
2230
2231         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2232
2233         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2234
2235         g_dhcp_client_start(dhcp->dhcp_client, NULL);
2236
2237         return FALSE;
2238 }
2239
2240 static gboolean start_pd_rebind(gpointer user_data)
2241 {
2242         struct connman_dhcpv6 *dhcp = user_data;
2243
2244         if (check_pd_restart(dhcp) < 0)
2245                 return FALSE;
2246
2247         dhcp->RT = initial_rt(REB_TIMEOUT);
2248
2249         DBG("rebind initial RT timeout %d msec", dhcp->RT);
2250
2251         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2252
2253         dhcpv6_pd_rebind(dhcp);
2254
2255         return FALSE;
2256 }
2257
2258 static gboolean timeout_pd_rebind_confirm(gpointer user_data)
2259 {
2260         struct connman_dhcpv6 *dhcp = user_data;
2261
2262         dhcp->RT = calc_delay(dhcp->RT, CNF_MAX_RT);
2263
2264         DBG("rebind with confirm RT timeout %d msec", dhcp->RT);
2265
2266         dhcp->timeout = g_timeout_add(dhcp->RT,
2267                                         timeout_pd_rebind_confirm, dhcp);
2268
2269         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2270
2271         g_dhcp_client_start(dhcp->dhcp_client, NULL);
2272
2273         return FALSE;
2274 }
2275
2276 static gboolean timeout_pd_max_confirm(gpointer user_data)
2277 {
2278         struct connman_dhcpv6 *dhcp = user_data;
2279
2280         dhcp->MRD = 0;
2281
2282         clear_timer(dhcp);
2283
2284         DBG("rebind with confirm max retransmit duration timeout");
2285
2286         g_dhcpv6_client_clear_retransmit(dhcp->dhcp_client);
2287
2288         if (dhcp->callback)
2289                 dhcp->callback(dhcp->network,
2290                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2291
2292         return FALSE;
2293 }
2294
2295 static gboolean start_pd_rebind_with_confirm(gpointer user_data)
2296 {
2297         struct connman_dhcpv6 *dhcp = user_data;
2298
2299         dhcp->RT = initial_rt(CNF_TIMEOUT);
2300
2301         DBG("rebind with confirm initial RT timeout %d msec", dhcp->RT);
2302
2303         dhcp->timeout = g_timeout_add(dhcp->RT,
2304                                         timeout_pd_rebind_confirm, dhcp);
2305         dhcp->MRD = g_timeout_add(CNF_MAX_RD, timeout_pd_max_confirm, dhcp);
2306
2307         dhcpv6_pd_rebind(dhcp);
2308
2309         return FALSE;
2310 }
2311
2312 static gboolean timeout_pd_renew(gpointer user_data)
2313 {
2314         struct connman_dhcpv6 *dhcp = user_data;
2315
2316         if (check_pd_restart(dhcp) < 0)
2317                 return FALSE;
2318
2319         dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
2320
2321         DBG("renew RT timeout %d msec", dhcp->RT);
2322
2323         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
2324
2325         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2326
2327         g_dhcp_client_start(dhcp->dhcp_client, NULL);
2328
2329         return FALSE;
2330 }
2331
2332 static gboolean start_pd_renew(gpointer user_data)
2333 {
2334         struct connman_dhcpv6 *dhcp = user_data;
2335
2336         dhcp->RT = initial_rt(REN_TIMEOUT);
2337
2338         DBG("renew initial RT timeout %d msec", dhcp->RT);
2339
2340         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_renew, dhcp);
2341
2342         dhcpv6_pd_renew(dhcp);
2343
2344         return FALSE;
2345 }
2346
2347 int __connman_dhcpv6_start_pd_renew(struct connman_network *network,
2348                                 dhcpv6_cb callback)
2349 {
2350         struct connman_dhcpv6 *dhcp;
2351         uint32_t T1, T2;
2352         time_t started, current, expired;
2353
2354         dhcp = g_hash_table_lookup(network_pd_table, network);
2355         if (!dhcp)
2356                 return -ENOENT;
2357
2358         DBG("network %p dhcp %p", network, dhcp);
2359
2360         clear_timer(dhcp);
2361
2362         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
2363                                 &started, &expired);
2364
2365         current = time(NULL);
2366
2367         DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
2368                 expired, current, started);
2369
2370         if (T1 == 0xffffffff)
2371                 /* RFC 3633, ch 9 */
2372                 return 0;
2373
2374         if (T1 == 0)
2375                 /* RFC 3633, ch 9
2376                  * Client can choose the timeout.
2377                  */
2378                 T1 = 120;
2379
2380         dhcp->callback = callback;
2381
2382         /* RFC 3315, 18.1.4, start solicit if expired */
2383         if (check_pd_restart(dhcp) < 0)
2384                 return 0;
2385
2386         if (T2 != 0xffffffff && T2 > 0) {
2387                 if ((unsigned)current >= (unsigned)started + T2) {
2388                         /* RFC 3315, chapter 18.1.3, start rebind */
2389                         DBG("rebind after %d secs", T2);
2390
2391                         dhcp->timeout = g_timeout_add_seconds(T2,
2392                                                         start_pd_rebind,
2393                                                         dhcp);
2394
2395                 } else if ((unsigned)current < (unsigned)started + T1) {
2396                         DBG("renew after %d secs", T1);
2397
2398                         dhcp->timeout = g_timeout_add_seconds(T1,
2399                                                         start_pd_renew,
2400                                                         dhcp);
2401                 } else {
2402                         DBG("rebind after %d secs", T2 - T1);
2403
2404                         dhcp->timeout = g_timeout_add_seconds(T2 - T1,
2405                                                         start_pd_rebind,
2406                                                         dhcp);
2407                 }
2408         }
2409
2410         return 0;
2411 }
2412
2413 static void release_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2414 {
2415         DBG("");
2416 }
2417
2418 int __connman_dhcpv6_start_pd_release(struct connman_network *network,
2419                                 dhcpv6_cb callback)
2420 {
2421         struct connman_dhcpv6 *dhcp;
2422         GDHCPClient *dhcp_client;
2423         uint32_t T1, T2;
2424
2425         if (!network_table)
2426                 return 0;   /* we are already released */
2427
2428         dhcp = g_hash_table_lookup(network_pd_table, network);
2429         if (!dhcp)
2430                 return -ENOENT;
2431
2432         DBG("network %p dhcp %p client %p", network, dhcp, dhcp->dhcp_client);
2433
2434         clear_timer(dhcp);
2435
2436         dhcp_client = dhcp->dhcp_client;
2437         if (!dhcp_client) {
2438                 DBG("DHCPv6 PD was not started");
2439                 return 0;
2440         }
2441
2442         g_dhcp_client_clear_requests(dhcp_client);
2443         g_dhcp_client_clear_values(dhcp_client);
2444
2445         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2446         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2447
2448         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2449         g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2450
2451         clear_callbacks(dhcp_client);
2452
2453         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RELEASE,
2454                                         release_pd_cb, dhcp);
2455
2456         dhcp->dhcp_client = dhcp_client;
2457
2458         return g_dhcp_client_start(dhcp_client, NULL);
2459 }
2460
2461 static gboolean timeout_pd_request(gpointer user_data)
2462 {
2463         struct connman_dhcpv6 *dhcp = user_data;
2464
2465         if (dhcp->request_count >= REQ_MAX_RC) {
2466                 DBG("max request retry attempts %d", dhcp->request_count);
2467                 dhcp->request_count = 0;
2468                 if (dhcp->callback)
2469                         dhcp->callback(dhcp->network,
2470                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2471                 return FALSE;
2472         }
2473
2474         dhcp->request_count++;
2475
2476         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
2477         DBG("request RT timeout %d msec", dhcp->RT);
2478         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2479
2480         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2481
2482         g_dhcp_client_start(dhcp->dhcp_client, NULL);
2483
2484         return FALSE;
2485 }
2486
2487 static void request_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2488 {
2489         struct connman_dhcpv6 *dhcp = user_data;
2490         uint16_t status;
2491
2492         DBG("");
2493
2494         g_dhcpv6_client_clear_retransmit(dhcp_client);
2495
2496         status = g_dhcpv6_client_get_status(dhcp_client);
2497
2498         DBG("dhcpv6 pd cb msg %p status %d", dhcp, status);
2499
2500         if (status  == G_DHCPV6_ERROR_BINDING) {
2501                 /* RFC 3315, 18.1.8 */
2502                 dhcpv6_pd_request(dhcp);
2503         } else {
2504                 set_prefixes(dhcp_client, dhcp);
2505         }
2506 }
2507
2508 static int dhcpv6_pd_request(struct connman_dhcpv6 *dhcp)
2509 {
2510         GDHCPClient *dhcp_client;
2511         uint32_t T1 = 0, T2 = 0;
2512
2513         DBG("dhcp %p", dhcp);
2514
2515         dhcp_client = dhcp->dhcp_client;
2516
2517         g_dhcp_client_clear_requests(dhcp_client);
2518
2519         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2520         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2521         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2522         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2523
2524         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2525         g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2526
2527         clear_callbacks(dhcp_client);
2528
2529         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
2530                                         request_pd_cb, dhcp);
2531
2532         return g_dhcp_client_start(dhcp_client, NULL);
2533 }
2534
2535 static void advertise_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2536 {
2537         struct connman_dhcpv6 *dhcp = user_data;
2538
2539         DBG("dhcpv6 advertise pd msg %p", dhcp);
2540
2541         clear_timer(dhcp);
2542
2543         g_dhcpv6_client_clear_retransmit(dhcp_client);
2544
2545         if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
2546                 if (dhcp->callback)
2547                         dhcp->callback(dhcp->network,
2548                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2549                 return;
2550         }
2551
2552         dhcp->RT = initial_rt(REQ_TIMEOUT);
2553         DBG("request initial RT timeout %d msec", dhcp->RT);
2554         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2555
2556         dhcp->request_count = 1;
2557
2558         dhcpv6_pd_request(dhcp);
2559 }
2560
2561 static void solicitation_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2562 {
2563         /*
2564          * This callback is here so that g_dhcp_client_start()
2565          * will enter the proper L3 mode.
2566          */
2567         DBG("DHCPv6 %p solicitation msg received, ignoring it", user_data);
2568 }
2569
2570 static int dhcpv6_pd_solicitation(struct connman_dhcpv6 *dhcp)
2571 {
2572         GDHCPClient *dhcp_client;
2573         int ret;
2574
2575         DBG("dhcp %p", dhcp);
2576
2577         dhcp_client = create_pd_client(dhcp, &ret);
2578         if (!dhcp_client) {
2579                 clear_timer(dhcp);
2580                 return ret;
2581         }
2582
2583         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2584
2585         g_dhcpv6_client_set_pd(dhcp_client, NULL, NULL, NULL);
2586
2587         clear_callbacks(dhcp_client);
2588
2589         g_dhcp_client_register_event(dhcp_client,
2590                                 G_DHCP_CLIENT_EVENT_ADVERTISE,
2591                                 advertise_pd_cb, dhcp);
2592
2593         g_dhcp_client_register_event(dhcp_client,
2594                                 G_DHCP_CLIENT_EVENT_SOLICITATION,
2595                                 solicitation_pd_cb, dhcp);
2596
2597         dhcp->dhcp_client = dhcp_client;
2598
2599         return g_dhcp_client_start(dhcp_client, NULL);
2600 }
2601
2602 static gboolean start_pd_solicitation(gpointer user_data)
2603 {
2604         struct connman_dhcpv6 *dhcp = user_data;
2605
2606         /* Set the retransmission timeout, RFC 3315 chapter 14 */
2607         dhcp->RT = initial_rt(SOL_TIMEOUT);
2608
2609         DBG("solicit initial RT timeout %d msec", dhcp->RT);
2610
2611         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
2612
2613         dhcpv6_pd_solicitation(dhcp);
2614
2615         return FALSE;
2616 }
2617
2618 int __connman_dhcpv6_start_pd(int index, GSList *prefixes, dhcpv6_cb callback)
2619 {
2620         struct connman_service *service;
2621         struct connman_network *network;
2622         struct connman_dhcpv6 *dhcp;
2623
2624         if (index < 0)
2625                 return 0;
2626
2627         DBG("index %d", index);
2628
2629         service = __connman_service_lookup_from_index(index);
2630         if (!service)
2631                 return -EINVAL;
2632
2633         network = __connman_service_get_network(service);
2634         if (!network)
2635                 return -EINVAL;
2636
2637         if (network_pd_table) {
2638                 dhcp = g_hash_table_lookup(network_pd_table, network);
2639                 if (dhcp && dhcp->started)
2640                         return -EBUSY;
2641         }
2642
2643         dhcp = g_try_new0(struct connman_dhcpv6, 1);
2644         if (!dhcp)
2645                 return -ENOMEM;
2646
2647         dhcp->network = network;
2648         dhcp->callback = callback;
2649         dhcp->started = true;
2650
2651         if (!prefixes) {
2652                 /*
2653                  * Try to load the earlier prefixes if caller did not supply
2654                  * any that we could use.
2655                  */
2656                 struct connman_ipconfig *ipconfig;
2657                 ipconfig = __connman_service_get_ip6config(service);
2658
2659                 dhcp->prefixes = load_prefixes(ipconfig);
2660         } else
2661                 dhcp->prefixes = prefixes;
2662
2663         connman_network_ref(network);
2664
2665         DBG("replace network %p dhcp %p", network, dhcp);
2666
2667         g_hash_table_replace(network_pd_table, network, dhcp);
2668
2669         if (!dhcp->prefixes) {
2670                 /*
2671                  * Refresh start, try to get prefixes.
2672                  */
2673                 start_pd_solicitation(dhcp);
2674         } else {
2675                 /*
2676                  * We used to have prefixes, try to use them again.
2677                  * We need to use timeouts from confirm msg, RFC 3633, ch 12.1
2678                  */
2679                 start_pd_rebind_with_confirm(dhcp);
2680         }
2681
2682         return 0;
2683 }
2684
2685 void __connman_dhcpv6_stop_pd(int index)
2686 {
2687         struct connman_service *service;
2688         struct connman_network *network;
2689
2690         if (index < 0)
2691                 return;
2692
2693         DBG("index %d", index);
2694
2695         if (!network_pd_table)
2696                 return;
2697
2698         service = __connman_service_lookup_from_index(index);
2699         if (!service)
2700                 return;
2701
2702         network = __connman_service_get_network(service);
2703         if (!network)
2704                 return;
2705
2706         __connman_dhcpv6_start_pd_release(network, NULL);
2707
2708         if (g_hash_table_remove(network_pd_table, network))
2709                 connman_network_unref(network);
2710 }
2711
2712 int __connman_dhcpv6_init(void)
2713 {
2714         DBG("");
2715
2716         network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2717                                                         NULL, remove_network);
2718
2719         network_pd_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2720                                                         NULL, remove_network);
2721
2722         return 0;
2723 }
2724
2725 void __connman_dhcpv6_cleanup(void)
2726 {
2727         DBG("");
2728
2729         g_hash_table_destroy(network_table);
2730         network_table = NULL;
2731
2732         g_hash_table_destroy(network_pd_table);
2733         network_pd_table = NULL;
2734 }