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