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