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