Removed unnecessary logs
[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         current = time(NULL);
1238
1239         if (current >= expired) {
1240                 DBG("expired by %d secs", (int)(current - expired));
1241
1242                 g_timeout_add(0, dhcpv6_restart, dhcp);
1243
1244                 return -ETIMEDOUT;
1245         }
1246
1247         return 0;
1248 }
1249
1250 static gboolean timeout_rebind(gpointer user_data)
1251 {
1252         struct connman_dhcpv6 *dhcp = user_data;
1253
1254         if (check_restart(dhcp) < 0)
1255                 return FALSE;
1256
1257         dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
1258
1259         DBG("rebind RT timeout %d msec", dhcp->RT);
1260
1261         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1262
1263         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1264
1265         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1266
1267         return FALSE;
1268 }
1269
1270 static gboolean start_rebind(gpointer user_data)
1271 {
1272         struct connman_dhcpv6 *dhcp = user_data;
1273
1274         if (check_restart(dhcp) < 0)
1275                 return FALSE;
1276
1277         dhcp->RT = initial_rt(REB_TIMEOUT);
1278
1279         DBG("rebind initial RT timeout %d msec", dhcp->RT);
1280
1281         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1282
1283         dhcpv6_rebind(dhcp);
1284
1285         return FALSE;
1286 }
1287
1288 static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
1289 {
1290         DBG("");
1291
1292         g_dhcpv6_client_reset_request(dhcp_client);
1293         g_dhcpv6_client_clear_retransmit(dhcp_client);
1294
1295         re_cb(REQ_REQUEST, dhcp_client, user_data);
1296 }
1297
1298 static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
1299                         bool add_addresses)
1300 {
1301         GDHCPClient *dhcp_client;
1302         uint32_t T1, T2;
1303
1304         DBG("dhcp %p add %d", dhcp, add_addresses);
1305
1306         dhcp_client = dhcp->dhcp_client;
1307
1308         g_dhcp_client_clear_requests(dhcp_client);
1309
1310         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1311         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1312         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1313         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1314         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1315
1316         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1317                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1318
1319         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1320         g_dhcpv6_client_set_ia(dhcp_client,
1321                         connman_network_get_index(dhcp->network),
1322                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1323                         &T1, &T2, add_addresses, NULL);
1324
1325         clear_callbacks(dhcp_client);
1326
1327         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
1328                                         request_cb, dhcp);
1329
1330         dhcp->dhcp_client = dhcp_client;
1331
1332         return g_dhcp_client_start(dhcp_client, NULL);
1333 }
1334
1335 static gboolean timeout_request(gpointer user_data)
1336 {
1337         struct connman_dhcpv6 *dhcp = user_data;
1338
1339         if (dhcp->request_count >= REQ_MAX_RC) {
1340                 DBG("max request retry attempts %d", dhcp->request_count);
1341                 dhcp->request_count = 0;
1342                 if (dhcp->callback)
1343                         dhcp->callback(dhcp->network,
1344                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1345                 return FALSE;
1346         }
1347
1348         dhcp->request_count++;
1349
1350         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1351         DBG("request RT timeout %d msec", dhcp->RT);
1352         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1353
1354         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1355
1356         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1357
1358         return FALSE;
1359 }
1360
1361 static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
1362 {
1363         DBG("");
1364
1365         g_dhcpv6_client_reset_request(dhcp_client);
1366         g_dhcpv6_client_clear_retransmit(dhcp_client);
1367
1368         re_cb(REQ_RENEW, dhcp_client, user_data);
1369 }
1370
1371 static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
1372 {
1373         GDHCPClient *dhcp_client;
1374         uint32_t T1, T2;
1375
1376         DBG("dhcp %p", dhcp);
1377
1378         dhcp_client = dhcp->dhcp_client;
1379
1380         g_dhcp_client_clear_requests(dhcp_client);
1381
1382         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1383         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1384         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1385         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1386         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1387
1388         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1389                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1390
1391         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1392         g_dhcpv6_client_set_ia(dhcp_client,
1393                         connman_network_get_index(dhcp->network),
1394                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1395                         &T1, &T2, TRUE, NULL);
1396
1397         clear_callbacks(dhcp_client);
1398
1399         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
1400                                         renew_cb, dhcp);
1401
1402         dhcp->dhcp_client = dhcp_client;
1403
1404         return g_dhcp_client_start(dhcp_client, NULL);
1405 }
1406
1407 static gboolean timeout_renew(gpointer user_data)
1408 {
1409         struct connman_dhcpv6 *dhcp = user_data;
1410         time_t last_rebind, current;
1411         uint32_t T2;
1412
1413         if (check_restart(dhcp) < 0)
1414                 return FALSE;
1415
1416         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, &T2,
1417                                 &last_rebind, NULL);
1418         current = time(NULL);
1419         if ((unsigned)current >= (unsigned)last_rebind + T2) {
1420                 /*
1421                  * Do rebind instead if past T2
1422                  */
1423                 start_rebind(dhcp);
1424                 return FALSE;
1425         }
1426
1427         dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
1428
1429         DBG("renew RT timeout %d msec", dhcp->RT);
1430
1431         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1432
1433         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1434
1435         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1436
1437         return FALSE;
1438 }
1439
1440 static gboolean start_renew(gpointer user_data)
1441 {
1442         struct connman_dhcpv6 *dhcp = user_data;
1443
1444         dhcp->RT = initial_rt(REN_TIMEOUT);
1445
1446         DBG("renew initial RT timeout %d msec", dhcp->RT);
1447
1448         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1449
1450         dhcpv6_renew(dhcp);
1451
1452         return FALSE;
1453 }
1454
1455 int __connman_dhcpv6_start_renew(struct connman_network *network,
1456                                                         dhcpv6_cb callback)
1457 {
1458         struct connman_dhcpv6 *dhcp;
1459         uint32_t T1, T2, delta;
1460         time_t started, current, expired;
1461
1462         dhcp = g_hash_table_lookup(network_table, network);
1463         if (!dhcp)
1464                 return -ENOENT;
1465
1466         DBG("network %p dhcp %p", network, dhcp);
1467
1468         clear_timer(dhcp);
1469
1470         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
1471                                 &started, &expired);
1472
1473         current = time(NULL);
1474
1475         DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
1476                 (unsigned long)expired, current, started);
1477
1478         if (T1 == 0xffffffff)
1479                 /* RFC 3315, 22.4 */
1480                 return 0;
1481
1482         if (T1 == 0) {
1483                 /* RFC 3315, 22.4
1484                  * Client can choose the timeout.
1485                  */
1486                 T1 = (expired - started) / 2;
1487                 T2 = (expired - started) / 10 * 8;
1488         }
1489
1490         dhcp->callback = callback;
1491
1492         /* RFC 3315, 18.1.4, start solicit if expired */
1493         if (check_restart(dhcp) < 0)
1494                 return 0;
1495
1496         if (T2 != 0xffffffff && T2 > 0) {
1497                 if ((unsigned)current >= (unsigned)started + T2) {
1498                         /* RFC 3315, chapter 18.1.3, start rebind */
1499                         DBG("start rebind immediately");
1500
1501                         dhcp->timeout = g_timeout_add_seconds(0, start_rebind,
1502                                                         dhcp);
1503
1504                 } else if ((unsigned)current < (unsigned)started + T1) {
1505                         delta = started + T1 - current;
1506                         DBG("renew after %d secs", delta);
1507
1508                         dhcp->timeout = g_timeout_add_seconds(delta,
1509                                         start_renew, dhcp);
1510                 } else {
1511                         delta = started + T2 - current;
1512                         DBG("rebind after %d secs", delta);
1513
1514                         dhcp->timeout = g_timeout_add_seconds(delta,
1515                                         start_rebind, dhcp);
1516                 }
1517         }
1518
1519         return 0;
1520 }
1521
1522 static void release_cb(GDHCPClient *dhcp_client, gpointer user_data)
1523 {
1524         DBG("");
1525 }
1526
1527 int __connman_dhcpv6_start_release(struct connman_network *network,
1528                                 dhcpv6_cb callback)
1529 {
1530         struct connman_dhcpv6 *dhcp;
1531         GDHCPClient *dhcp_client;
1532
1533         if (!network_table)
1534                 return 0;   /* we are already released */
1535
1536         dhcp = g_hash_table_lookup(network_table, network);
1537         if (!dhcp)
1538                 return -ENOENT;
1539
1540         DBG("network %p dhcp %p client %p stateless %d", network, dhcp,
1541                                         dhcp->dhcp_client, dhcp->stateless);
1542
1543         if (dhcp->stateless)
1544                 return -EINVAL;
1545
1546         clear_timer(dhcp);
1547
1548         dhcp_client = dhcp->dhcp_client;
1549         if (!dhcp_client) {
1550                 /*
1551                  * We had started the DHCPv6 handshaking i.e., we have called
1552                  * __connman_dhcpv6_start() but it has not yet sent
1553                  * a solicitation message to server. This means that we do not
1554                  * have DHCPv6 configured yet so we can just quit here.
1555                  */
1556                 DBG("DHCPv6 was not started");
1557                 return 0;
1558         }
1559
1560         g_dhcp_client_clear_requests(dhcp_client);
1561         g_dhcp_client_clear_values(dhcp_client);
1562
1563         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1564         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1565
1566         g_dhcpv6_client_set_ia(dhcp_client,
1567                         connman_network_get_index(dhcp->network),
1568                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1569                         NULL, NULL, TRUE, NULL);
1570
1571         clear_callbacks(dhcp_client);
1572
1573         /*
1574          * Although we register a callback here we are really not interested in
1575          * the answer because it might take too long time and network code
1576          * might be in the middle of the disconnect.
1577          * So we just inform the server that we are done with the addresses
1578          * but ignore the reply from server. This is allowed by RFC 3315
1579          * chapter 18.1.6.
1580          */
1581         g_dhcp_client_register_event(dhcp_client,
1582                                 G_DHCP_CLIENT_EVENT_RELEASE,
1583                                 release_cb, NULL);
1584
1585         dhcp->dhcp_client = dhcp_client;
1586
1587         return g_dhcp_client_start(dhcp_client, NULL);
1588 }
1589
1590 static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
1591 {
1592         DBG("dhcp %p", dhcp);
1593
1594         clear_timer(dhcp);
1595
1596         dhcpv6_free(dhcp);
1597
1598         if (!dhcp->dhcp_client)
1599                 return 0;
1600
1601         g_dhcp_client_stop(dhcp->dhcp_client);
1602         g_dhcp_client_unref(dhcp->dhcp_client);
1603
1604         dhcp->dhcp_client = NULL;
1605
1606         return 0;
1607 }
1608
1609 static void remove_network(gpointer user_data)
1610 {
1611         struct connman_dhcpv6 *dhcp = user_data;
1612
1613         DBG("dhcp %p", dhcp);
1614
1615         dhcpv6_release(dhcp);
1616
1617         g_free(dhcp);
1618 }
1619
1620 static gboolean timeout_info_req(gpointer user_data)
1621 {
1622         struct connman_dhcpv6 *dhcp = user_data;
1623
1624         dhcp->RT = calc_delay(dhcp->RT, INF_MAX_RT);
1625
1626         DBG("info RT timeout %d msec", dhcp->RT);
1627
1628         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1629
1630         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1631
1632         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1633
1634         return FALSE;
1635 }
1636
1637 static gboolean start_info_req(gpointer user_data)
1638 {
1639         struct connman_dhcpv6 *dhcp = user_data;
1640
1641         /* Set the retransmission timeout, RFC 3315 chapter 14 */
1642         dhcp->RT = initial_rt(INF_TIMEOUT);
1643
1644         DBG("info initial RT timeout %d msec", dhcp->RT);
1645
1646         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1647
1648         dhcpv6_info_request(dhcp);
1649
1650         return FALSE;
1651 }
1652
1653 int __connman_dhcpv6_start_info(struct connman_network *network,
1654                                 dhcpv6_cb callback)
1655 {
1656         struct connman_dhcpv6 *dhcp;
1657         int delay;
1658         uint64_t rand;
1659
1660         DBG("");
1661
1662         if (network_table) {
1663                 dhcp = g_hash_table_lookup(network_table, network);
1664                 if (dhcp && dhcp->started)
1665                         return -EBUSY;
1666         }
1667
1668         dhcp = g_try_new0(struct connman_dhcpv6, 1);
1669         if (!dhcp)
1670                 return -ENOMEM;
1671
1672         dhcp->network = network;
1673         dhcp->callback = callback;
1674         dhcp->stateless = true;
1675         dhcp->started = true;
1676
1677         connman_network_ref(network);
1678
1679         DBG("replace network %p dhcp %p", network, dhcp);
1680
1681         g_hash_table_replace(network_table, network, dhcp);
1682
1683         /* Initial timeout, RFC 3315, 18.1.5 */
1684         __connman_util_get_random(&rand);
1685         delay = rand % 1000;
1686
1687         dhcp->timeout = g_timeout_add(delay, start_info_req, dhcp);
1688
1689         return 0;
1690 }
1691
1692 static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data)
1693 {
1694         struct connman_dhcpv6 *dhcp = user_data;
1695
1696         DBG("dhcpv6 advertise msg %p", dhcp);
1697
1698         clear_timer(dhcp);
1699
1700         g_dhcpv6_client_clear_retransmit(dhcp_client);
1701
1702         if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1703                 if (dhcp->callback)
1704                         dhcp->callback(dhcp->network,
1705                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1706                 return;
1707         }
1708
1709         dhcp->RT = initial_rt(REQ_TIMEOUT);
1710         DBG("request initial RT timeout %d msec", dhcp->RT);
1711         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1712
1713         dhcp->request_count = 1;
1714
1715         dhcpv6_request(dhcp, true);
1716 }
1717
1718 static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)
1719 {
1720         /* We get here if server supports rapid commit */
1721         struct connman_dhcpv6 *dhcp = user_data;
1722
1723         DBG("dhcpv6 solicitation msg %p", dhcp);
1724
1725         clear_timer(dhcp);
1726
1727         g_dhcpv6_client_clear_retransmit(dhcp_client);
1728
1729         if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1730                 if (dhcp->callback)
1731                         dhcp->callback(dhcp->network,
1732                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1733                 return;
1734         }
1735
1736         do_dad(dhcp_client, dhcp);
1737 }
1738
1739 static gboolean timeout_solicitation(gpointer user_data)
1740 {
1741         struct connman_dhcpv6 *dhcp = user_data;
1742
1743         dhcp->RT = calc_delay(dhcp->RT, SOL_MAX_RT);
1744
1745         DBG("solicit RT timeout %d msec", dhcp->RT);
1746
1747         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1748
1749         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1750
1751         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1752
1753         return FALSE;
1754 }
1755
1756 static int dhcpv6_solicitation(struct connman_dhcpv6 *dhcp)
1757 {
1758         struct connman_service *service;
1759         struct connman_ipconfig *ipconfig_ipv6;
1760         GDHCPClient *dhcp_client;
1761         GDHCPClientError error;
1762         int index, ret;
1763
1764         DBG("dhcp %p", dhcp);
1765
1766         index = connman_network_get_index(dhcp->network);
1767
1768         dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
1769         if (error != G_DHCP_CLIENT_ERROR_NONE) {
1770                 clear_timer(dhcp);
1771                 return -EINVAL;
1772         }
1773
1774 #if !defined TIZEN_EXT
1775         if (getenv("CONNMAN_DHCPV6_DEBUG"))
1776 #endif
1777                 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
1778
1779         service = connman_service_lookup_from_network(dhcp->network);
1780         if (!service) {
1781                 clear_timer(dhcp);
1782                 g_dhcp_client_unref(dhcp_client);
1783                 return -EINVAL;
1784         }
1785
1786         ret = set_duid(service, dhcp->network, dhcp_client, index);
1787         if (ret < 0) {
1788                 clear_timer(dhcp);
1789                 g_dhcp_client_unref(dhcp_client);
1790                 return ret;
1791         }
1792
1793         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1794         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
1795         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1796         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1797         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1798
1799         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1800                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1801
1802         ipconfig_ipv6 = __connman_service_get_ip6config(service);
1803         dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
1804
1805         g_dhcpv6_client_set_ia(dhcp_client, index,
1806                         dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1807                         NULL, NULL, FALSE, NULL);
1808
1809         clear_callbacks(dhcp_client);
1810
1811         g_dhcp_client_register_event(dhcp_client,
1812                                 G_DHCP_CLIENT_EVENT_SOLICITATION,
1813                                 solicitation_cb, dhcp);
1814
1815         g_dhcp_client_register_event(dhcp_client,
1816                                 G_DHCP_CLIENT_EVENT_ADVERTISE,
1817                                 advertise_cb, dhcp);
1818
1819         dhcp->dhcp_client = dhcp_client;
1820
1821         return g_dhcp_client_start(dhcp_client, NULL);
1822 }
1823
1824 static gboolean start_solicitation(gpointer user_data)
1825 {
1826         struct connman_dhcpv6 *dhcp = user_data;
1827
1828         /* Set the retransmission timeout, RFC 3315 chapter 14 */
1829         dhcp->RT = initial_rt(SOL_TIMEOUT);
1830
1831         DBG("solicit initial RT timeout %d msec", dhcp->RT);
1832
1833         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1834
1835         dhcpv6_solicitation(dhcp);
1836
1837         return FALSE;
1838 }
1839
1840 int __connman_dhcpv6_start(struct connman_network *network,
1841                                 GSList *prefixes, dhcpv6_cb callback)
1842 {
1843         struct connman_service *service;
1844         struct connman_dhcpv6 *dhcp;
1845         int delay;
1846         uint64_t rand;
1847
1848         DBG("");
1849
1850         if (network_table) {
1851                 dhcp = g_hash_table_lookup(network_table, network);
1852                 if (dhcp && dhcp->started)
1853                         return -EBUSY;
1854         }
1855
1856         service = connman_service_lookup_from_network(network);
1857         if (!service)
1858                 return -EINVAL;
1859
1860         dhcp = g_try_new0(struct connman_dhcpv6, 1);
1861         if (!dhcp)
1862                 return -ENOMEM;
1863
1864         dhcp->network = network;
1865         dhcp->callback = callback;
1866         dhcp->prefixes = prefixes;
1867         dhcp->started = true;
1868
1869         connman_network_ref(network);
1870
1871         DBG("replace network %p dhcp %p", network, dhcp);
1872
1873         g_hash_table_replace(network_table, network, dhcp);
1874
1875         /* Initial timeout, RFC 3315, 17.1.2 */
1876         __connman_util_get_random(&rand);
1877         delay = rand % 1000;
1878
1879         /*
1880          * Start from scratch.
1881          * RFC 3315, chapter 17.1.2 Solicitation message
1882          *
1883          * Note that we do not send CONFIRM message here as it does
1884          * not make much sense because we do not save expiration time
1885          * so we cannot really know how long the saved address is valid
1886          * anyway. The reply to CONFIRM message does not send
1887          * expiration times back to us. Because of this we need to
1888          * start using SOLICITATION anyway.
1889          */
1890         dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
1891
1892         return 0;
1893 }
1894
1895 void __connman_dhcpv6_stop(struct connman_network *network)
1896 {
1897         DBG("");
1898
1899         if (!network_table)
1900                 return;
1901
1902         if (g_hash_table_remove(network_table, network))
1903                 connman_network_unref(network);
1904 }
1905
1906 static int save_prefixes(struct connman_ipconfig *ipconfig,
1907                         GSList *prefixes)
1908 {
1909         GSList *list;
1910         int i = 0, count = g_slist_length(prefixes);
1911         char **array;
1912
1913         if (count == 0)
1914                 return 0;
1915
1916         array = g_try_new0(char *, count + 1);
1917         if (!array)
1918                 return -ENOMEM;
1919
1920         for (list = prefixes; list; list = list->next) {
1921                 char *elem, addr_str[INET6_ADDRSTRLEN];
1922                 GDHCPIAPrefix *prefix = list->data;
1923
1924                 elem = g_strdup_printf("%s/%d", inet_ntop(AF_INET6,
1925                                 &prefix->prefix, addr_str, INET6_ADDRSTRLEN),
1926                                 prefix->prefixlen);
1927                 if (!elem) {
1928                         g_strfreev(array);
1929                         return -ENOMEM;
1930                 }
1931
1932                 array[i++] = elem;
1933         }
1934
1935         __connman_ipconfig_set_dhcpv6_prefixes(ipconfig, array);
1936         return 0;
1937 }
1938
1939 static GSList *load_prefixes(struct connman_ipconfig *ipconfig)
1940 {
1941         int i;
1942         GSList *list = NULL;
1943         char **array =  __connman_ipconfig_get_dhcpv6_prefixes(ipconfig);
1944
1945         if (!array)
1946                 return NULL;
1947
1948         for (i = 0; array[i]; i++) {
1949                 GDHCPIAPrefix *prefix;
1950                 long int value;
1951                 char *ptr, **elems = g_strsplit(array[i], "/", 0);
1952
1953                 if (!elems)
1954                         return list;
1955
1956                 value = strtol(elems[1], &ptr, 10);
1957                 if (ptr != elems[1] && *ptr == '\0' && value <= 128) {
1958                         struct in6_addr addr;
1959
1960                         if (inet_pton(AF_INET6, elems[0], &addr) == 1) {
1961                                 prefix = g_try_new0(GDHCPIAPrefix, 1);
1962                                 if (!prefix) {
1963                                         g_strfreev(elems);
1964                                         return list;
1965                                 }
1966                                 memcpy(&prefix->prefix, &addr,
1967                                         sizeof(struct in6_addr));
1968                                 prefix->prefixlen = value;
1969
1970                                 list = g_slist_prepend(list, prefix);
1971                         }
1972                 }
1973
1974                 g_strfreev(elems);
1975         }
1976
1977         return list;
1978 }
1979
1980 static GDHCPIAPrefix *copy_prefix(gpointer data)
1981 {
1982         GDHCPIAPrefix *copy, *prefix = data;
1983
1984         copy = g_try_new(GDHCPIAPrefix, 1);
1985         if (!copy)
1986                 return NULL;
1987
1988         memcpy(copy, prefix, sizeof(GDHCPIAPrefix));
1989
1990         return copy;
1991 }
1992
1993 static GSList *copy_and_convert_prefixes(GList *prefixes)
1994 {
1995         GSList *copy = NULL;
1996         GList *list;
1997
1998         for (list = prefixes; list; list = list->next)
1999                 copy = g_slist_prepend(copy, copy_prefix(list->data));
2000
2001         return copy;
2002 }
2003
2004 static int set_prefixes(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
2005 {
2006         if (dhcp->prefixes)
2007                 g_slist_free_full(dhcp->prefixes, free_prefix);
2008
2009         dhcp->prefixes =
2010                 copy_and_convert_prefixes(g_dhcp_client_get_option(dhcp_client,
2011                                                         G_DHCPV6_IA_PD));
2012
2013         DBG("Got %d prefix", g_slist_length(dhcp->prefixes));
2014
2015         if (dhcp->callback) {
2016                 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
2017                 if (status == G_DHCPV6_ERROR_NO_PREFIX)
2018                         dhcp->callback(dhcp->network,
2019                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2020                 else {
2021                         struct connman_service *service;
2022                         struct connman_ipconfig *ipconfig;
2023                         int ifindex = connman_network_get_index(dhcp->network);
2024
2025                         service = __connman_service_lookup_from_index(ifindex);
2026                         if (service) {
2027                                 ipconfig = __connman_service_get_ip6config(
2028                                                                 service);
2029                                 save_prefixes(ipconfig, dhcp->prefixes);
2030                                 __connman_service_save(service);
2031                         }
2032
2033                         dhcp->callback(dhcp->network,
2034                                 CONNMAN_DHCPV6_STATUS_SUCCEED, dhcp->prefixes);
2035                 }
2036         } else {
2037                 g_slist_free_full(dhcp->prefixes, free_prefix);
2038                 dhcp->prefixes = NULL;
2039         }
2040
2041         return 0;
2042 }
2043
2044 static void re_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2045 {
2046         struct connman_dhcpv6 *dhcp = user_data;
2047         uint16_t status;
2048         int ret;
2049
2050         status = g_dhcpv6_client_get_status(dhcp_client);
2051
2052         DBG("dhcpv6 cb msg %p status %d", dhcp, status);
2053
2054         if (status  == G_DHCPV6_ERROR_BINDING) {
2055                 /* RFC 3315, 18.1.8 */
2056                 dhcpv6_pd_request(dhcp);
2057         } else {
2058                 ret = set_prefixes(dhcp_client, dhcp);
2059                 if (ret < 0) {
2060                         if (dhcp->callback)
2061                                 dhcp->callback(dhcp->network,
2062                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2063                         return;
2064                 }
2065         }
2066 }
2067
2068 static void rebind_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2069 {
2070         DBG("");
2071
2072         g_dhcpv6_client_clear_retransmit(dhcp_client);
2073
2074         re_pd_cb(dhcp_client, user_data);
2075 }
2076
2077 static GDHCPClient *create_pd_client(struct connman_dhcpv6 *dhcp, int *err)
2078 {
2079         GDHCPClient *dhcp_client;
2080         GDHCPClientError error;
2081         struct connman_service *service;
2082         int index, ret;
2083         uint32_t iaid;
2084
2085         index = connman_network_get_index(dhcp->network);
2086
2087         dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
2088         if (error != G_DHCP_CLIENT_ERROR_NONE) {
2089                 *err = -EINVAL;
2090                 return NULL;
2091         }
2092
2093 #if !defined TIZEN_EXT
2094         if (getenv("CONNMAN_DHCPV6_DEBUG"))
2095 #endif
2096                 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6:PD");
2097
2098         service = connman_service_lookup_from_network(dhcp->network);
2099         if (!service) {
2100                 g_dhcp_client_unref(dhcp_client);
2101                 *err = -EINVAL;
2102                 return NULL;
2103         }
2104
2105         ret = set_duid(service, dhcp->network, dhcp_client, index);
2106         if (ret < 0) {
2107                 g_dhcp_client_unref(dhcp_client);
2108                 *err = ret;
2109                 return NULL;
2110         }
2111
2112         g_dhcpv6_client_create_iaid(dhcp_client, index, (unsigned char *)&iaid);
2113         g_dhcpv6_client_set_iaid(dhcp_client, iaid);
2114
2115         return dhcp_client;
2116 }
2117
2118 static int dhcpv6_pd_rebind(struct connman_dhcpv6 *dhcp)
2119 {
2120         GDHCPClient *dhcp_client;
2121         uint32_t T1, T2;
2122
2123         DBG("dhcp %p", dhcp);
2124
2125         if (!dhcp->dhcp_client) {
2126                 /*
2127                  * We skipped the solicitation phase
2128                  */
2129                 int err;
2130
2131                 dhcp->dhcp_client = create_pd_client(dhcp, &err);
2132                 if (!dhcp->dhcp_client) {
2133                         clear_timer(dhcp);
2134                         return err;
2135                 }
2136         }
2137
2138         dhcp_client = dhcp->dhcp_client;
2139
2140         g_dhcp_client_clear_requests(dhcp_client);
2141
2142         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2143         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2144         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2145
2146         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2147         g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2148
2149         clear_callbacks(dhcp_client);
2150
2151         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
2152                                         rebind_pd_cb, dhcp);
2153
2154         return g_dhcp_client_start(dhcp_client, NULL);
2155 }
2156
2157 static void renew_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2158 {
2159         DBG("");
2160
2161         g_dhcpv6_client_clear_retransmit(dhcp_client);
2162
2163         re_pd_cb(dhcp_client, user_data);
2164 }
2165
2166 static int dhcpv6_pd_renew(struct connman_dhcpv6 *dhcp)
2167 {
2168         GDHCPClient *dhcp_client;
2169         uint32_t T1, T2;
2170
2171         DBG("dhcp %p", dhcp);
2172
2173         dhcp_client = dhcp->dhcp_client;
2174
2175         g_dhcp_client_clear_requests(dhcp_client);
2176
2177         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2178         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2179         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2180         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2181
2182         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2183         g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2184
2185         clear_callbacks(dhcp_client);
2186
2187         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
2188                                         renew_pd_cb, dhcp);
2189
2190         return g_dhcp_client_start(dhcp_client, NULL);
2191 }
2192
2193 /*
2194  * Check if we need to restart the solicitation procedure. This
2195  * is done if all the prefixes have expired.
2196  */
2197 static int check_pd_restart(struct connman_dhcpv6 *dhcp)
2198 {
2199         time_t current, expired;
2200
2201         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
2202                                 NULL, &expired);
2203         current = time(NULL);
2204
2205         if (current > expired) {
2206                 DBG("expired by %d secs", (int)(current - expired));
2207
2208                 g_timeout_add(0, dhcpv6_restart, dhcp);
2209
2210                 return -ETIMEDOUT;
2211         }
2212
2213         return 0;
2214 }
2215
2216 static gboolean timeout_pd_rebind(gpointer user_data)
2217 {
2218         struct connman_dhcpv6 *dhcp = user_data;
2219
2220         if (check_pd_restart(dhcp) < 0)
2221                 return FALSE;
2222
2223         dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
2224
2225         DBG("rebind RT timeout %d msec", dhcp->RT);
2226
2227         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2228
2229         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2230
2231         g_dhcp_client_start(dhcp->dhcp_client, NULL);
2232
2233         return FALSE;
2234 }
2235
2236 static gboolean start_pd_rebind(gpointer user_data)
2237 {
2238         struct connman_dhcpv6 *dhcp = user_data;
2239
2240         if (check_pd_restart(dhcp) < 0)
2241                 return FALSE;
2242
2243         dhcp->RT = initial_rt(REB_TIMEOUT);
2244
2245         DBG("rebind initial RT timeout %d msec", dhcp->RT);
2246
2247         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2248
2249         dhcpv6_pd_rebind(dhcp);
2250
2251         return FALSE;
2252 }
2253
2254 static gboolean timeout_pd_rebind_confirm(gpointer user_data)
2255 {
2256         struct connman_dhcpv6 *dhcp = user_data;
2257
2258         dhcp->RT = calc_delay(dhcp->RT, CNF_MAX_RT);
2259
2260         DBG("rebind with confirm RT timeout %d msec", dhcp->RT);
2261
2262         dhcp->timeout = g_timeout_add(dhcp->RT,
2263                                         timeout_pd_rebind_confirm, dhcp);
2264
2265         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2266
2267         g_dhcp_client_start(dhcp->dhcp_client, NULL);
2268
2269         return FALSE;
2270 }
2271
2272 static gboolean timeout_pd_max_confirm(gpointer user_data)
2273 {
2274         struct connman_dhcpv6 *dhcp = user_data;
2275
2276         dhcp->MRD = 0;
2277
2278         clear_timer(dhcp);
2279
2280         DBG("rebind with confirm max retransmit duration timeout");
2281
2282         g_dhcpv6_client_clear_retransmit(dhcp->dhcp_client);
2283
2284         if (dhcp->callback)
2285                 dhcp->callback(dhcp->network,
2286                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2287
2288         return FALSE;
2289 }
2290
2291 static gboolean start_pd_rebind_with_confirm(gpointer user_data)
2292 {
2293         struct connman_dhcpv6 *dhcp = user_data;
2294
2295         dhcp->RT = initial_rt(CNF_TIMEOUT);
2296
2297         DBG("rebind with confirm initial RT timeout %d msec", dhcp->RT);
2298
2299         dhcp->timeout = g_timeout_add(dhcp->RT,
2300                                         timeout_pd_rebind_confirm, dhcp);
2301         dhcp->MRD = g_timeout_add(CNF_MAX_RD, timeout_pd_max_confirm, dhcp);
2302
2303         dhcpv6_pd_rebind(dhcp);
2304
2305         return FALSE;
2306 }
2307
2308 static gboolean timeout_pd_renew(gpointer user_data)
2309 {
2310         struct connman_dhcpv6 *dhcp = user_data;
2311
2312         if (check_pd_restart(dhcp) < 0)
2313                 return FALSE;
2314
2315         dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
2316
2317         DBG("renew RT timeout %d msec", dhcp->RT);
2318
2319         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
2320
2321         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2322
2323         g_dhcp_client_start(dhcp->dhcp_client, NULL);
2324
2325         return FALSE;
2326 }
2327
2328 static gboolean start_pd_renew(gpointer user_data)
2329 {
2330         struct connman_dhcpv6 *dhcp = user_data;
2331
2332         dhcp->RT = initial_rt(REN_TIMEOUT);
2333
2334         DBG("renew initial RT timeout %d msec", dhcp->RT);
2335
2336         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_renew, dhcp);
2337
2338         dhcpv6_pd_renew(dhcp);
2339
2340         return FALSE;
2341 }
2342
2343 int __connman_dhcpv6_start_pd_renew(struct connman_network *network,
2344                                 dhcpv6_cb callback)
2345 {
2346         struct connman_dhcpv6 *dhcp;
2347         uint32_t T1, T2;
2348         time_t started, current, expired;
2349
2350         dhcp = g_hash_table_lookup(network_pd_table, network);
2351         if (!dhcp)
2352                 return -ENOENT;
2353
2354         DBG("network %p dhcp %p", network, dhcp);
2355
2356         clear_timer(dhcp);
2357
2358         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
2359                                 &started, &expired);
2360
2361         current = time(NULL);
2362
2363         DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
2364                 expired, current, started);
2365
2366         if (T1 == 0xffffffff)
2367                 /* RFC 3633, ch 9 */
2368                 return 0;
2369
2370         if (T1 == 0)
2371                 /* RFC 3633, ch 9
2372                  * Client can choose the timeout.
2373                  */
2374                 T1 = 120;
2375
2376         dhcp->callback = callback;
2377
2378         /* RFC 3315, 18.1.4, start solicit if expired */
2379         if (check_pd_restart(dhcp) < 0)
2380                 return 0;
2381
2382         if (T2 != 0xffffffff && T2 > 0) {
2383                 if ((unsigned)current >= (unsigned)started + T2) {
2384                         /* RFC 3315, chapter 18.1.3, start rebind */
2385                         DBG("rebind after %d secs", T2);
2386
2387                         dhcp->timeout = g_timeout_add_seconds(T2,
2388                                                         start_pd_rebind,
2389                                                         dhcp);
2390
2391                 } else if ((unsigned)current < (unsigned)started + T1) {
2392                         DBG("renew after %d secs", T1);
2393
2394                         dhcp->timeout = g_timeout_add_seconds(T1,
2395                                                         start_pd_renew,
2396                                                         dhcp);
2397                 } else {
2398                         DBG("rebind after %d secs", T2 - T1);
2399
2400                         dhcp->timeout = g_timeout_add_seconds(T2 - T1,
2401                                                         start_pd_rebind,
2402                                                         dhcp);
2403                 }
2404         }
2405
2406         return 0;
2407 }
2408
2409 static void release_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2410 {
2411         DBG("");
2412 }
2413
2414 int __connman_dhcpv6_start_pd_release(struct connman_network *network,
2415                                 dhcpv6_cb callback)
2416 {
2417         struct connman_dhcpv6 *dhcp;
2418         GDHCPClient *dhcp_client;
2419         uint32_t T1, T2;
2420
2421         if (!network_table)
2422                 return 0;   /* we are already released */
2423
2424         dhcp = g_hash_table_lookup(network_pd_table, network);
2425         if (!dhcp)
2426                 return -ENOENT;
2427
2428         DBG("network %p dhcp %p client %p", network, dhcp, dhcp->dhcp_client);
2429
2430         clear_timer(dhcp);
2431
2432         dhcp_client = dhcp->dhcp_client;
2433         if (!dhcp_client) {
2434                 DBG("DHCPv6 PD was not started");
2435                 return 0;
2436         }
2437
2438         g_dhcp_client_clear_requests(dhcp_client);
2439         g_dhcp_client_clear_values(dhcp_client);
2440
2441         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2442         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2443
2444         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2445         g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2446
2447         clear_callbacks(dhcp_client);
2448
2449         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RELEASE,
2450                                         release_pd_cb, dhcp);
2451
2452         dhcp->dhcp_client = dhcp_client;
2453
2454         return g_dhcp_client_start(dhcp_client, NULL);
2455 }
2456
2457 static gboolean timeout_pd_request(gpointer user_data)
2458 {
2459         struct connman_dhcpv6 *dhcp = user_data;
2460
2461         if (dhcp->request_count >= REQ_MAX_RC) {
2462                 DBG("max request retry attempts %d", dhcp->request_count);
2463                 dhcp->request_count = 0;
2464                 if (dhcp->callback)
2465                         dhcp->callback(dhcp->network,
2466                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2467                 return FALSE;
2468         }
2469
2470         dhcp->request_count++;
2471
2472         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
2473         DBG("request RT timeout %d msec", dhcp->RT);
2474         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2475
2476         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2477
2478         g_dhcp_client_start(dhcp->dhcp_client, NULL);
2479
2480         return FALSE;
2481 }
2482
2483 static void request_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2484 {
2485         struct connman_dhcpv6 *dhcp = user_data;
2486         uint16_t status;
2487
2488         DBG("");
2489
2490         g_dhcpv6_client_clear_retransmit(dhcp_client);
2491
2492         status = g_dhcpv6_client_get_status(dhcp_client);
2493
2494         DBG("dhcpv6 pd cb msg %p status %d", dhcp, status);
2495
2496         if (status  == G_DHCPV6_ERROR_BINDING) {
2497                 /* RFC 3315, 18.1.8 */
2498                 dhcpv6_pd_request(dhcp);
2499         } else {
2500                 set_prefixes(dhcp_client, dhcp);
2501         }
2502 }
2503
2504 static int dhcpv6_pd_request(struct connman_dhcpv6 *dhcp)
2505 {
2506         GDHCPClient *dhcp_client;
2507         uint32_t T1 = 0, T2 = 0;
2508
2509         DBG("dhcp %p", dhcp);
2510
2511         dhcp_client = dhcp->dhcp_client;
2512
2513         g_dhcp_client_clear_requests(dhcp_client);
2514
2515         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2516         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2517         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2518         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2519
2520         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2521         g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2522
2523         clear_callbacks(dhcp_client);
2524
2525         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
2526                                         request_pd_cb, dhcp);
2527
2528         return g_dhcp_client_start(dhcp_client, NULL);
2529 }
2530
2531 static void advertise_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2532 {
2533         struct connman_dhcpv6 *dhcp = user_data;
2534
2535         DBG("dhcpv6 advertise pd msg %p", dhcp);
2536
2537         clear_timer(dhcp);
2538
2539         g_dhcpv6_client_clear_retransmit(dhcp_client);
2540
2541         if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
2542                 if (dhcp->callback)
2543                         dhcp->callback(dhcp->network,
2544                                         CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2545                 return;
2546         }
2547
2548         dhcp->RT = initial_rt(REQ_TIMEOUT);
2549         DBG("request initial RT timeout %d msec", dhcp->RT);
2550         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2551
2552         dhcp->request_count = 1;
2553
2554         dhcpv6_pd_request(dhcp);
2555 }
2556
2557 static void solicitation_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2558 {
2559         /*
2560          * This callback is here so that g_dhcp_client_start()
2561          * will enter the proper L3 mode.
2562          */
2563         DBG("DHCPv6 %p solicitation msg received, ignoring it", user_data);
2564 }
2565
2566 static int dhcpv6_pd_solicitation(struct connman_dhcpv6 *dhcp)
2567 {
2568         GDHCPClient *dhcp_client;
2569         int ret;
2570
2571         DBG("dhcp %p", dhcp);
2572
2573         dhcp_client = create_pd_client(dhcp, &ret);
2574         if (!dhcp_client) {
2575                 clear_timer(dhcp);
2576                 return ret;
2577         }
2578
2579         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2580
2581         g_dhcpv6_client_set_pd(dhcp_client, NULL, NULL, NULL);
2582
2583         clear_callbacks(dhcp_client);
2584
2585         g_dhcp_client_register_event(dhcp_client,
2586                                 G_DHCP_CLIENT_EVENT_ADVERTISE,
2587                                 advertise_pd_cb, dhcp);
2588
2589         g_dhcp_client_register_event(dhcp_client,
2590                                 G_DHCP_CLIENT_EVENT_SOLICITATION,
2591                                 solicitation_pd_cb, dhcp);
2592
2593         dhcp->dhcp_client = dhcp_client;
2594
2595         return g_dhcp_client_start(dhcp_client, NULL);
2596 }
2597
2598 static gboolean start_pd_solicitation(gpointer user_data)
2599 {
2600         struct connman_dhcpv6 *dhcp = user_data;
2601
2602         /* Set the retransmission timeout, RFC 3315 chapter 14 */
2603         dhcp->RT = initial_rt(SOL_TIMEOUT);
2604
2605         DBG("solicit initial RT timeout %d msec", dhcp->RT);
2606
2607         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
2608
2609         dhcpv6_pd_solicitation(dhcp);
2610
2611         return FALSE;
2612 }
2613
2614 int __connman_dhcpv6_start_pd(int index, GSList *prefixes, dhcpv6_cb callback)
2615 {
2616         struct connman_service *service;
2617         struct connman_network *network;
2618         struct connman_dhcpv6 *dhcp;
2619
2620         if (index < 0)
2621                 return 0;
2622
2623         DBG("index %d", index);
2624
2625         service = __connman_service_lookup_from_index(index);
2626         if (!service)
2627                 return -EINVAL;
2628
2629         network = __connman_service_get_network(service);
2630         if (!network)
2631                 return -EINVAL;
2632
2633         if (network_pd_table) {
2634                 dhcp = g_hash_table_lookup(network_pd_table, network);
2635                 if (dhcp && dhcp->started)
2636                         return -EBUSY;
2637         }
2638
2639         dhcp = g_try_new0(struct connman_dhcpv6, 1);
2640         if (!dhcp)
2641                 return -ENOMEM;
2642
2643         dhcp->network = network;
2644         dhcp->callback = callback;
2645         dhcp->started = true;
2646
2647         if (!prefixes) {
2648                 /*
2649                  * Try to load the earlier prefixes if caller did not supply
2650                  * any that we could use.
2651                  */
2652                 struct connman_ipconfig *ipconfig;
2653                 ipconfig = __connman_service_get_ip6config(service);
2654
2655                 dhcp->prefixes = load_prefixes(ipconfig);
2656         } else
2657                 dhcp->prefixes = prefixes;
2658
2659         connman_network_ref(network);
2660
2661         DBG("replace network %p dhcp %p", network, dhcp);
2662
2663         g_hash_table_replace(network_pd_table, network, dhcp);
2664
2665         if (!dhcp->prefixes) {
2666                 /*
2667                  * Refresh start, try to get prefixes.
2668                  */
2669                 start_pd_solicitation(dhcp);
2670         } else {
2671                 /*
2672                  * We used to have prefixes, try to use them again.
2673                  * We need to use timeouts from confirm msg, RFC 3633, ch 12.1
2674                  */
2675                 start_pd_rebind_with_confirm(dhcp);
2676         }
2677
2678         return 0;
2679 }
2680
2681 void __connman_dhcpv6_stop_pd(int index)
2682 {
2683         struct connman_service *service;
2684         struct connman_network *network;
2685
2686         if (index < 0)
2687                 return;
2688
2689         DBG("index %d", index);
2690
2691         if (!network_pd_table)
2692                 return;
2693
2694         service = __connman_service_lookup_from_index(index);
2695         if (!service)
2696                 return;
2697
2698         network = __connman_service_get_network(service);
2699         if (!network)
2700                 return;
2701
2702         __connman_dhcpv6_start_pd_release(network, NULL);
2703
2704         if (g_hash_table_remove(network_pd_table, network))
2705                 connman_network_unref(network);
2706 }
2707
2708 int __connman_dhcpv6_init(void)
2709 {
2710         DBG("");
2711
2712         network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2713                                                         NULL, remove_network);
2714
2715         network_pd_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2716                                                         NULL, remove_network);
2717
2718         return 0;
2719 }
2720
2721 void __connman_dhcpv6_cleanup(void)
2722 {
2723         DBG("");
2724
2725         g_hash_table_destroy(network_table);
2726         network_table = NULL;
2727
2728         g_hash_table_destroy(network_pd_table);
2729         network_pd_table = NULL;
2730 }