bff57d44c23e6082c74a5ac69c73542d07f580e1
[platform/upstream/connman.git] / src / dhcpv6.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2012  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
30 #include <connman/ipconfig.h>
31 #include <connman/storage.h>
32
33 #include <gdhcp/gdhcp.h>
34
35 #include <glib.h>
36
37 #include "connman.h"
38
39 /* Transmission params in msec, RFC 3315 chapter 5.5 */
40 #define INF_MAX_DELAY   (1 * 1000)
41 #define INF_TIMEOUT     (1 * 1000)
42 #define INF_MAX_RT      (120 * 1000)
43 #define SOL_MAX_DELAY   (1 * 1000)
44 #define SOL_TIMEOUT     (1 * 1000)
45 #define SOL_MAX_RT      (120 * 1000)
46 #define REQ_TIMEOUT     (1 * 1000)
47 #define REQ_MAX_RT      (30 * 1000)
48 #define REQ_MAX_RC      10
49 #define REN_TIMEOUT     (10 * 1000)
50 #define REN_MAX_RT      (600 * 1000)
51 #define REB_TIMEOUT     (10 * 1000)
52 #define REB_MAX_RT      (600 * 1000)
53 #define CNF_MAX_DELAY   (1 * 1000)
54 #define CNF_TIMEOUT     (1 * 1000)
55 #define CNF_MAX_RT      (4 * 1000)
56 #define CNF_MAX_RD      (10 * 1000)
57
58
59 struct connman_dhcpv6 {
60         struct connman_network *network;
61         dhcp_cb callback;
62
63         char **nameservers;
64         char **timeservers;
65
66         GDHCPClient *dhcp_client;
67
68         guint timeout;          /* operation timeout in msec */
69         guint MRD;              /* max operation timeout in msec */
70         guint RT;               /* in msec */
71         gboolean use_ta;        /* set to TRUE if IPv6 privacy is enabled */
72         GSList *prefixes;       /* network prefixes from radvd */
73         int request_count;      /* how many times REQUEST have been sent */
74         gboolean stateless;     /* TRUE if stateless DHCPv6 is used */
75         gboolean started;       /* TRUE if we have DHCPv6 started */
76 };
77
78 static GHashTable *network_table;
79
80 static int dhcpv6_request(struct connman_dhcpv6 *dhcp, gboolean add_addresses);
81
82 static void clear_timer(struct connman_dhcpv6 *dhcp)
83 {
84         if (dhcp->timeout > 0) {
85                 g_source_remove(dhcp->timeout);
86                 dhcp->timeout = 0;
87         }
88
89         if (dhcp->MRD > 0) {
90                 g_source_remove(dhcp->MRD);
91                 dhcp->MRD = 0;
92         }
93 }
94
95 static inline float get_random()
96 {
97         return (rand() % 200 - 100) / 1000.0;
98 }
99
100 /* Calculate a random delay, RFC 3315 chapter 14 */
101 /* RT and MRT are milliseconds */
102 static guint calc_delay(guint RT, guint MRT)
103 {
104         float delay = get_random();
105         float rt = RT * (2 + delay);
106
107         if (rt > MRT)
108                 rt = MRT * (1 + delay);
109
110         if (rt < 0)
111                 rt = MRT;
112
113         return (guint)rt;
114 }
115
116 static void free_prefix(gpointer data, gpointer user_data)
117 {
118         g_free(data);
119 }
120
121 static void dhcpv6_free(struct connman_dhcpv6 *dhcp)
122 {
123         g_strfreev(dhcp->nameservers);
124         g_strfreev(dhcp->timeservers);
125
126         dhcp->nameservers = NULL;
127         dhcp->timeservers = NULL;
128         dhcp->started = FALSE;
129
130         g_slist_foreach(dhcp->prefixes, free_prefix, NULL);
131         g_slist_free(dhcp->prefixes);
132 }
133
134 static gboolean compare_string_arrays(char **array_a, char **array_b)
135 {
136         int i;
137
138         if (array_a == NULL || array_b == NULL)
139                 return FALSE;
140
141         if (g_strv_length(array_a) != g_strv_length(array_b))
142                 return FALSE;
143
144         for (i = 0; array_a[i] != NULL && array_b[i] != NULL; i++)
145                 if (g_strcmp0(array_a[i], array_b[i]) != 0)
146                         return FALSE;
147
148         return TRUE;
149 }
150
151 static void dhcpv6_debug(const char *str, void *data)
152 {
153         connman_info("%s: %s\n", (const char *) data, str);
154 }
155
156 static gchar *convert_to_hex(unsigned char *buf, int len)
157 {
158         gchar *ret = g_try_malloc(len * 2 + 1);
159         int i;
160
161         for (i = 0; ret != NULL && i < len; i++)
162                 g_snprintf(ret + i * 2, 3, "%02x", buf[i]);
163
164         return ret;
165 }
166
167 /*
168  * DUID should not change over time so save it to file.
169  * See RFC 3315 chapter 9 for details.
170  */
171 static int set_duid(struct connman_service *service,
172                         struct connman_network *network,
173                         GDHCPClient *dhcp_client, int index)
174 {
175         GKeyFile *keyfile;
176         const char *ident;
177         char *hex_duid;
178         unsigned char *duid;
179         int duid_len;
180
181         ident = __connman_service_get_ident(service);
182
183         keyfile = connman_storage_load_service(ident);
184         if (keyfile == NULL)
185                 return -EINVAL;
186
187         hex_duid = g_key_file_get_string(keyfile, ident, "IPv6.DHCP.DUID",
188                                         NULL);
189         if (hex_duid != NULL) {
190                 unsigned int i, j = 0, hex;
191                 size_t hex_duid_len = strlen(hex_duid);
192
193                 duid = g_try_malloc0(hex_duid_len / 2);
194                 if (duid == NULL) {
195                         g_key_file_free(keyfile);
196                         g_free(hex_duid);
197                         return -ENOMEM;
198                 }
199
200                 for (i = 0; i < hex_duid_len; i += 2) {
201                         sscanf(hex_duid + i, "%02x", &hex);
202                         duid[j++] = hex;
203                 }
204
205                 duid_len = hex_duid_len / 2;
206         } else {
207                 int ret;
208                 int type = __connman_ipconfig_get_type_from_index(index);
209
210                 ret = g_dhcpv6_create_duid(G_DHCPV6_DUID_LLT, index, type,
211                                         &duid, &duid_len);
212                 if (ret < 0) {
213                         g_key_file_free(keyfile);
214                         return ret;
215                 }
216
217                 hex_duid = convert_to_hex(duid, duid_len);
218                 if (hex_duid == NULL) {
219                         g_key_file_free(keyfile);
220                         return -ENOMEM;
221                 }
222
223                 g_key_file_set_string(keyfile, ident, "IPv6.DHCP.DUID",
224                                 hex_duid);
225
226                 __connman_storage_save_service(keyfile, ident);
227         }
228         g_free(hex_duid);
229
230         g_key_file_free(keyfile);
231
232         g_dhcpv6_client_set_duid(dhcp_client, duid, duid_len);
233
234         return 0;
235 }
236
237 static void clear_callbacks(GDHCPClient *dhcp_client)
238 {
239         g_dhcp_client_register_event(dhcp_client,
240                                 G_DHCP_CLIENT_EVENT_SOLICITATION,
241                                 NULL, NULL);
242
243         g_dhcp_client_register_event(dhcp_client,
244                                 G_DHCP_CLIENT_EVENT_ADVERTISE,
245                                 NULL, NULL);
246
247         g_dhcp_client_register_event(dhcp_client,
248                                 G_DHCP_CLIENT_EVENT_REQUEST,
249                                 NULL, NULL);
250
251         g_dhcp_client_register_event(dhcp_client,
252                                 G_DHCP_CLIENT_EVENT_CONFIRM,
253                                 NULL, NULL);
254
255         g_dhcp_client_register_event(dhcp_client,
256                                 G_DHCP_CLIENT_EVENT_RENEW,
257                                 NULL, NULL);
258
259         g_dhcp_client_register_event(dhcp_client,
260                                 G_DHCP_CLIENT_EVENT_REBIND,
261                                 NULL, NULL);
262
263         g_dhcp_client_register_event(dhcp_client,
264                                 G_DHCP_CLIENT_EVENT_RELEASE,
265                                 NULL, NULL);
266
267         g_dhcp_client_register_event(dhcp_client,
268                                 G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
269                                 NULL, NULL);
270 }
271
272 static void info_req_cb(GDHCPClient *dhcp_client, gpointer user_data)
273 {
274         struct connman_dhcpv6 *dhcp = user_data;
275         struct connman_service *service;
276         int entries, i;
277         GList *option, *list;
278         char **nameservers, **timeservers;
279
280         DBG("dhcpv6 information-request %p", dhcp);
281
282         service = connman_service_lookup_from_network(dhcp->network);
283         if (service == NULL) {
284                 connman_error("Can not lookup service");
285                 return;
286         }
287
288         g_dhcpv6_client_clear_retransmit(dhcp_client);
289
290         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
291         entries = g_list_length(option);
292
293         nameservers = g_try_new0(char *, entries + 1);
294         if (nameservers != NULL) {
295                 for (i = 0, list = option; list; list = list->next, i++)
296                         nameservers[i] = g_strdup(list->data);
297         }
298
299         if (compare_string_arrays(nameservers, dhcp->nameservers) == FALSE) {
300                 if (dhcp->nameservers != NULL) {
301                         for (i = 0; dhcp->nameservers[i] != NULL; i++)
302                                 __connman_service_nameserver_remove(service,
303                                                         dhcp->nameservers[i],
304                                                         FALSE);
305                         g_strfreev(dhcp->nameservers);
306                 }
307
308                 dhcp->nameservers = nameservers;
309
310                 for (i = 0; dhcp->nameservers != NULL &&
311                                         dhcp->nameservers[i] != NULL; i++)
312                         __connman_service_nameserver_append(service,
313                                                 dhcp->nameservers[i],
314                                                 FALSE);
315         } else
316                 g_strfreev(nameservers);
317
318
319         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
320         entries = g_list_length(option);
321
322         timeservers = g_try_new0(char *, entries + 1);
323         if (timeservers != NULL) {
324                 for (i = 0, list = option; list; list = list->next, i++)
325                         timeservers[i] = g_strdup(list->data);
326         }
327
328         if (compare_string_arrays(timeservers, dhcp->timeservers) == FALSE) {
329                 if (dhcp->timeservers != NULL) {
330                         for (i = 0; dhcp->timeservers[i] != NULL; i++)
331                                 __connman_service_timeserver_remove(service,
332                                                         dhcp->timeservers[i]);
333                         g_strfreev(dhcp->timeservers);
334                 }
335
336                 dhcp->timeservers = timeservers;
337
338                 for (i = 0; dhcp->timeservers != NULL &&
339                                         dhcp->timeservers[i] != NULL; i++)
340                         __connman_service_timeserver_append(service,
341                                                         dhcp->timeservers[i]);
342         } else
343                 g_strfreev(timeservers);
344
345
346         if (dhcp->callback != NULL) {
347                 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
348                 dhcp->callback(dhcp->network, status == 0 ? TRUE : FALSE);
349         }
350 }
351
352 static int dhcpv6_info_request(struct connman_dhcpv6 *dhcp)
353 {
354         struct connman_service *service;
355         GDHCPClient *dhcp_client;
356         GDHCPClientError error;
357         int index, ret;
358
359         DBG("dhcp %p", dhcp);
360
361         index = connman_network_get_index(dhcp->network);
362
363         dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
364         if (error != G_DHCP_CLIENT_ERROR_NONE) {
365                 clear_timer(dhcp);
366                 return -EINVAL;
367         }
368
369         if (getenv("CONNMAN_DHCPV6_DEBUG"))
370                 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
371
372         service = connman_service_lookup_from_network(dhcp->network);
373         if (service == NULL) {
374                 clear_timer(dhcp);
375                 g_dhcp_client_unref(dhcp_client);
376                 return -EINVAL;
377         }
378
379         ret = set_duid(service, dhcp->network, dhcp_client, index);
380         if (ret < 0) {
381                 clear_timer(dhcp);
382                 g_dhcp_client_unref(dhcp_client);
383                 return ret;
384         }
385
386         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
387         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
388         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
389         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
390
391         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
392                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
393
394         g_dhcp_client_register_event(dhcp_client,
395                         G_DHCP_CLIENT_EVENT_INFORMATION_REQ, info_req_cb, dhcp);
396
397         dhcp->dhcp_client = dhcp_client;
398
399         return g_dhcp_client_start(dhcp_client, NULL);
400 }
401
402 static int check_ipv6_addr_prefix(GSList *prefixes, char *address)
403 {
404         struct in6_addr addr_prefix, addr;
405         GSList *list;
406         int ret = 128, len;
407
408         for (list = prefixes; list; list = list->next) {
409                 char *prefix = list->data;
410                 const char *slash = g_strrstr(prefix, "/");
411                 const unsigned char bits[] = { 0x00, 0xFE, 0xFC, 0xF8,
412                                                 0xF0, 0xE0, 0xC0, 0x80 };
413                 int left, count, i, plen;
414
415                 if (slash == NULL)
416                         continue;
417
418                 prefix = g_strndup(prefix, slash - prefix);
419                 len = strtol(slash + 1, NULL, 10);
420                 plen = 128 - len;
421
422                 count = plen / 8;
423                 left = plen % 8;
424                 i = 16 - count;
425
426                 inet_pton(AF_INET6, prefix, &addr_prefix);
427                 inet_pton(AF_INET6, address, &addr);
428
429                 memset(&addr_prefix.s6_addr[i], 0, count);
430                 memset(&addr.s6_addr[i], 0, count);
431
432                 if (left) {
433                         addr_prefix.s6_addr[i - 1] &= bits[left];
434                         addr.s6_addr[i - 1] &= bits[left];
435                 }
436
437                 g_free(prefix);
438
439                 if (memcmp(&addr_prefix, &addr, 16) == 0) {
440                         ret = len;
441                         break;
442                 }
443         }
444
445         return ret;
446 }
447
448 static int set_addresses(GDHCPClient *dhcp_client,
449                                                 struct connman_dhcpv6 *dhcp)
450 {
451         struct connman_service *service;
452         struct connman_ipconfig *ipconfig;
453         int entries, i;
454         GList *option, *list;
455         char **nameservers, **timeservers;
456         const char *c_address;
457         char *address = NULL;
458
459         service = connman_service_lookup_from_network(dhcp->network);
460         if (service == NULL) {
461                 connman_error("Can not lookup service");
462                 return -EINVAL;
463         }
464
465         ipconfig = __connman_service_get_ip6config(service);
466         if (ipconfig == NULL) {
467                 connman_error("Could not lookup ip6config");
468                 return -EINVAL;
469         }
470
471         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
472         entries = g_list_length(option);
473
474         nameservers = g_try_new0(char *, entries + 1);
475         if (nameservers != NULL) {
476                 for (i = 0, list = option; list; list = list->next, i++)
477                         nameservers[i] = g_strdup(list->data);
478         }
479
480         if (compare_string_arrays(nameservers, dhcp->nameservers) == FALSE) {
481                 if (dhcp->nameservers != NULL) {
482                         for (i = 0; dhcp->nameservers[i] != NULL; i++)
483                                 __connman_service_nameserver_remove(service,
484                                                         dhcp->nameservers[i],
485                                                         FALSE);
486                         g_strfreev(dhcp->nameservers);
487                 }
488
489                 dhcp->nameservers = nameservers;
490
491                 for (i = 0; dhcp->nameservers != NULL &&
492                                         dhcp->nameservers[i] != NULL; i++)
493                         __connman_service_nameserver_append(service,
494                                                         dhcp->nameservers[i],
495                                                         FALSE);
496         } else
497                 g_strfreev(nameservers);
498
499
500         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
501         entries = g_list_length(option);
502
503         timeservers = g_try_new0(char *, entries + 1);
504         if (timeservers != NULL) {
505                 for (i = 0, list = option; list; list = list->next, i++)
506                         timeservers[i] = g_strdup(list->data);
507         }
508
509         if (compare_string_arrays(timeservers, dhcp->timeservers) == FALSE) {
510                 if (dhcp->timeservers != NULL) {
511                         for (i = 0; dhcp->timeservers[i] != NULL; i++)
512                                 __connman_service_timeserver_remove(service,
513                                                         dhcp->timeservers[i]);
514                         g_strfreev(dhcp->timeservers);
515                 }
516
517                 dhcp->timeservers = timeservers;
518
519                 for (i = 0; dhcp->timeservers != NULL &&
520                                         dhcp->timeservers[i] != NULL; i++)
521                         __connman_service_timeserver_append(service,
522                                                         dhcp->timeservers[i]);
523         } else
524                 g_strfreev(timeservers);
525
526
527         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
528         if (option != NULL)
529                 address = g_strdup(option->data);
530         else {
531                 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
532                 if (option != NULL)
533                         address = g_strdup(option->data);
534         }
535
536         c_address = __connman_ipconfig_get_local(ipconfig);
537
538         if (address != NULL &&
539                         ((c_address != NULL &&
540                                 g_strcmp0(address, c_address) != 0) ||
541                         (c_address == NULL))) {
542                 int prefix_len;
543
544                 /* Is this prefix part of the subnet we are suppose to use? */
545                 prefix_len = check_ipv6_addr_prefix(dhcp->prefixes, address);
546
547                 __connman_ipconfig_set_local(ipconfig, address);
548                 __connman_ipconfig_set_prefixlen(ipconfig, prefix_len);
549
550                 DBG("new address %s/%d", address, prefix_len);
551
552                 __connman_ipconfig_set_dhcp_address(ipconfig, address);
553                 __connman_service_save(service);
554         }
555
556         g_free(address);
557
558         return 0;
559 }
560
561 static void re_cb(GDHCPClient *dhcp_client, gpointer user_data)
562 {
563         struct connman_dhcpv6 *dhcp = user_data;
564         uint16_t status;
565         int ret;
566
567         ret = set_addresses(dhcp_client, dhcp);
568
569         status = g_dhcpv6_client_get_status(dhcp_client);
570
571         DBG("dhcpv6 cb msg %p ret %d status %d", dhcp, ret, status);
572
573         if (ret < 0) {
574                 if (dhcp->callback != NULL)
575                         dhcp->callback(dhcp->network, FALSE);
576                 return;
577         }
578
579         if (status  == G_DHCPV6_ERROR_BINDING) {
580                 /* RFC 3315, 18.1.8 */
581                 dhcpv6_request(dhcp, FALSE);
582         } else {
583                 if (dhcp->callback != NULL)
584                         dhcp->callback(dhcp->network,
585                                                 status == 0 ? TRUE : FALSE);
586         }
587 }
588
589 static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data)
590 {
591         DBG("");
592
593         g_dhcpv6_client_reset_rebind(dhcp_client);
594         g_dhcpv6_client_reset_renew(dhcp_client);
595         g_dhcpv6_client_clear_retransmit(dhcp_client);
596
597         re_cb(dhcp_client, user_data);
598 }
599
600 static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp)
601 {
602         GDHCPClient *dhcp_client;
603
604         DBG("dhcp %p", dhcp);
605
606         dhcp_client = dhcp->dhcp_client;
607
608         g_dhcp_client_clear_requests(dhcp_client);
609
610         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
611         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
612         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
613         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
614
615         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
616                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
617
618         g_dhcpv6_client_set_ia(dhcp_client,
619                         connman_network_get_index(dhcp->network),
620                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
621                         NULL, NULL, FALSE, NULL);
622
623         clear_callbacks(dhcp_client);
624
625         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
626                                         rebind_cb, dhcp);
627
628         dhcp->dhcp_client = dhcp_client;
629
630         return g_dhcp_client_start(dhcp_client, NULL);
631 }
632
633 static gboolean dhcpv6_restart(gpointer user_data)
634 {
635         struct connman_dhcpv6 *dhcp = user_data;
636
637         if (dhcp->callback != NULL)
638                 dhcp->callback(dhcp->network, FALSE);
639
640         return FALSE;
641 }
642
643 /*
644  * Check if we need to restart the solicitation procedure. This
645  * is done if all the addresses have expired. RFC 3315, 18.1.4
646  */
647 static int check_restart(struct connman_dhcpv6 *dhcp)
648 {
649         time_t current, expired;
650
651         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
652                                 NULL, NULL, &expired);
653         current = time(NULL);
654
655         if (current > expired) {
656                 DBG("expired by %d secs", (int)(current - expired));
657
658                 g_timeout_add(0, dhcpv6_restart, dhcp);
659
660                 return -ETIMEDOUT;
661         }
662
663         return 0;
664 }
665
666 static gboolean timeout_rebind(gpointer user_data)
667 {
668         struct connman_dhcpv6 *dhcp = user_data;
669
670         if (check_restart(dhcp) < 0)
671                 return FALSE;
672
673         dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
674
675         DBG("rebind RT timeout %d msec", dhcp->RT);
676
677         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
678
679         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
680
681         g_dhcp_client_start(dhcp->dhcp_client, NULL);
682
683         return FALSE;
684 }
685
686 static gboolean start_rebind(gpointer user_data)
687 {
688         struct connman_dhcpv6 *dhcp = user_data;
689
690         dhcp->RT = REB_TIMEOUT * (1 + get_random());
691
692         DBG("rebind initial RT timeout %d msec", dhcp->RT);
693
694         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
695
696         dhcpv6_rebind(dhcp);
697
698         return FALSE;
699 }
700
701 static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
702 {
703         DBG("");
704
705         g_dhcpv6_client_clear_retransmit(dhcp_client);
706
707         re_cb(dhcp_client, user_data);
708 }
709
710 static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
711                         gboolean add_addresses)
712 {
713         GDHCPClient *dhcp_client;
714         uint32_t T1, T2;
715
716         DBG("dhcp %p add %d", dhcp, add_addresses);
717
718         dhcp_client = dhcp->dhcp_client;
719
720         g_dhcp_client_clear_requests(dhcp_client);
721
722         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
723         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
724         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
725         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
726         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
727
728         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
729                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
730
731         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL, NULL);
732         g_dhcpv6_client_set_ia(dhcp_client,
733                         connman_network_get_index(dhcp->network),
734                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
735                         &T1, &T2, add_addresses, NULL);
736
737         clear_callbacks(dhcp_client);
738
739         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
740                                         request_cb, dhcp);
741
742         dhcp->dhcp_client = dhcp_client;
743
744         return g_dhcp_client_start(dhcp_client, NULL);
745 }
746
747 static gboolean timeout_request(gpointer user_data)
748 {
749         struct connman_dhcpv6 *dhcp = user_data;
750
751         if (dhcp->request_count >= REQ_MAX_RC) {
752                 DBG("max request retry attempts %d", dhcp->request_count);
753                 dhcp->request_count = 0;
754                 if (dhcp->callback != NULL)
755                         dhcp->callback(dhcp->network, FALSE);
756                 return FALSE;
757         }
758
759         dhcp->request_count++;
760
761         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
762         DBG("request RT timeout %d msec", dhcp->RT);
763         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
764
765         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
766
767         g_dhcp_client_start(dhcp->dhcp_client, NULL);
768
769         return FALSE;
770 }
771
772 static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
773 {
774         DBG("");
775
776         g_dhcpv6_client_reset_renew(dhcp_client);
777         g_dhcpv6_client_clear_retransmit(dhcp_client);
778
779         re_cb(dhcp_client, user_data);
780 }
781
782 static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
783 {
784         GDHCPClient *dhcp_client;
785         uint32_t T1, T2;
786
787         DBG("dhcp %p", dhcp);
788
789         dhcp_client = dhcp->dhcp_client;
790
791         g_dhcp_client_clear_requests(dhcp_client);
792
793         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
794         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
795         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
796         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
797         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
798
799         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
800                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
801
802         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL, NULL);
803         g_dhcpv6_client_set_ia(dhcp_client,
804                         connman_network_get_index(dhcp->network),
805                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
806                         &T1, &T2, TRUE, NULL);
807
808         clear_callbacks(dhcp_client);
809
810         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
811                                         renew_cb, dhcp);
812
813         dhcp->dhcp_client = dhcp_client;
814
815         return g_dhcp_client_start(dhcp_client, NULL);
816 }
817
818 static gboolean timeout_renew(gpointer user_data)
819 {
820         struct connman_dhcpv6 *dhcp = user_data;
821
822         if (check_restart(dhcp) < 0)
823                 return FALSE;
824
825         dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
826
827         DBG("renew RT timeout %d msec", dhcp->RT);
828
829         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
830
831         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
832
833         g_dhcp_client_start(dhcp->dhcp_client, NULL);
834
835         return FALSE;
836 }
837
838 static gboolean start_renew(gpointer user_data)
839 {
840         struct connman_dhcpv6 *dhcp = user_data;
841
842         dhcp->RT = REN_TIMEOUT * (1 + get_random());
843
844         DBG("renew initial RT timeout %d msec", dhcp->RT);
845
846         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
847
848         dhcpv6_renew(dhcp);
849
850         return FALSE;
851 }
852
853 int __connman_dhcpv6_start_renew(struct connman_network *network,
854                                                         dhcp_cb callback)
855 {
856         struct connman_dhcpv6 *dhcp;
857         uint32_t T1, T2;
858         time_t last_renew, last_rebind, current, expired;
859
860         dhcp = g_hash_table_lookup(network_table, network);
861         if (dhcp == NULL)
862                 return -ENOENT;
863
864         DBG("network %p dhcp %p", network, dhcp);
865
866         clear_timer(dhcp);
867
868         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
869                                 &last_renew, &last_rebind, &expired);
870
871         current = time(NULL);
872
873         DBG("T1 %u T2 %u expires %lu current %lu", T1, T2,
874                 (unsigned long)expired, current);
875
876         if (T1 == 0xffffffff)
877                 /* RFC 3315, 22.4 */
878                 return 0;
879
880         if (T1 == 0)
881                 /* RFC 3315, 22.4
882                  * Client can choose the timeout.
883                  */
884                 T1 = 1800;
885
886         /* RFC 3315, 18.1.4, start solicit if expired */
887         if (current > expired) {
888                 DBG("expired by %d secs", (int)(current - expired));
889                 return -ETIMEDOUT;
890         }
891
892         dhcp->callback = callback;
893
894         if (T2 != 0xffffffff && T2 > 0 &&
895                         (unsigned)current > (unsigned)last_rebind + T2) {
896                 int timeout;
897
898                 /* RFC 3315, chapter 18.1.3, start rebind */
899                 if ((unsigned)current > (unsigned)last_renew + T1)
900                         timeout = 0;
901                 else
902                         timeout = last_renew - current + T1;
903
904                 /*
905                  * If we just did a renew, do not restart the rebind
906                  * immediately.
907                  */
908                 dhcp->timeout = g_timeout_add_seconds(timeout, start_rebind,
909                                                 dhcp);
910         } else {
911                 DBG("renew after %d secs", T1);
912
913                 dhcp->timeout = g_timeout_add_seconds(T1, start_renew, dhcp);
914         }
915         return 0;
916 }
917
918 static void release_cb(GDHCPClient *dhcp_client, gpointer user_data)
919 {
920         DBG("");
921 }
922
923 int __connman_dhcpv6_start_release(struct connman_network *network,
924                                 dhcp_cb callback)
925 {
926         struct connman_dhcpv6 *dhcp;
927         GDHCPClient *dhcp_client;
928
929         if (network_table == NULL)
930                 return 0;   /* we are already released */
931
932         dhcp = g_hash_table_lookup(network_table, network);
933         if (dhcp == NULL)
934                 return -ENOENT;
935
936         DBG("network %p dhcp %p client %p stateless %d", network, dhcp,
937                                         dhcp->dhcp_client, dhcp->stateless);
938
939         if (dhcp->stateless == TRUE)
940                 return -EINVAL;
941
942         clear_timer(dhcp);
943
944         dhcp_client = dhcp->dhcp_client;
945         if (dhcp_client == NULL) {
946                 /*
947                  * We had started the DHCPv6 handshaking i.e., we have called
948                  * __connman_dhcpv6_start() but it has not yet sent
949                  * a solicitation message to server. This means that we do not
950                  * have DHCPv6 configured yet so we can just quit here.
951                  */
952                 DBG("DHCPv6 was not started");
953                 return 0;
954         }
955
956         g_dhcp_client_clear_requests(dhcp_client);
957         g_dhcp_client_clear_values(dhcp_client);
958
959         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
960         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
961
962         g_dhcpv6_client_set_ia(dhcp_client,
963                         connman_network_get_index(dhcp->network),
964                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
965                         NULL, NULL, TRUE, NULL);
966
967         clear_callbacks(dhcp_client);
968
969         /*
970          * Although we register a callback here we are really not interested in
971          * the answer because it might take too long time and network code
972          * might be in the middle of the disconnect.
973          * So we just inform the server that we are done with the addresses
974          * but ignore the reply from server. This is allowed by RFC 3315
975          * chapter 18.1.6.
976          */
977         g_dhcp_client_register_event(dhcp_client,
978                                 G_DHCP_CLIENT_EVENT_RELEASE,
979                                 release_cb, NULL);
980
981         dhcp->dhcp_client = dhcp_client;
982
983         return g_dhcp_client_start(dhcp_client, NULL);
984 }
985
986 static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
987 {
988         DBG("dhcp %p", dhcp);
989
990         clear_timer(dhcp);
991
992         dhcpv6_free(dhcp);
993
994         if (dhcp->dhcp_client == NULL)
995                 return 0;
996
997         g_dhcp_client_stop(dhcp->dhcp_client);
998         g_dhcp_client_unref(dhcp->dhcp_client);
999
1000         dhcp->dhcp_client = NULL;
1001
1002         return 0;
1003 }
1004
1005 static void remove_network(gpointer user_data)
1006 {
1007         struct connman_dhcpv6 *dhcp = user_data;
1008
1009         DBG("dhcp %p", dhcp);
1010
1011         dhcpv6_release(dhcp);
1012
1013         g_free(dhcp);
1014 }
1015
1016 static gboolean timeout_info_req(gpointer user_data)
1017 {
1018         struct connman_dhcpv6 *dhcp = user_data;
1019
1020         dhcp->RT = calc_delay(dhcp->RT, INF_MAX_RT);
1021
1022         DBG("info RT timeout %d msec", dhcp->RT);
1023
1024         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1025
1026         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1027
1028         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1029
1030         return FALSE;
1031 }
1032
1033 static gboolean start_info_req(gpointer user_data)
1034 {
1035         struct connman_dhcpv6 *dhcp = user_data;
1036
1037         /* Set the retransmission timeout, RFC 3315 chapter 14 */
1038         dhcp->RT = INF_TIMEOUT * (1 + get_random());
1039
1040         DBG("info initial RT timeout %d msec", dhcp->RT);
1041
1042         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1043
1044         dhcpv6_info_request(dhcp);
1045
1046         return FALSE;
1047 }
1048
1049 int __connman_dhcpv6_start_info(struct connman_network *network,
1050                                 dhcp_cb callback)
1051 {
1052         struct connman_dhcpv6 *dhcp;
1053         int delay;
1054
1055         DBG("");
1056
1057         if (network_table != NULL) {
1058                 dhcp = g_hash_table_lookup(network_table, network);
1059                 if (dhcp != NULL && dhcp->started == TRUE)
1060                         return -EBUSY;
1061         }
1062
1063         dhcp = g_try_new0(struct connman_dhcpv6, 1);
1064         if (dhcp == NULL)
1065                 return -ENOMEM;
1066
1067         dhcp->network = network;
1068         dhcp->callback = callback;
1069         dhcp->stateless = TRUE;
1070         dhcp->started = TRUE;
1071
1072         connman_network_ref(network);
1073
1074         DBG("replace network %p dhcp %p", network, dhcp);
1075
1076         g_hash_table_replace(network_table, network, dhcp);
1077
1078         /* Initial timeout, RFC 3315, 18.1.5 */
1079         delay = rand() % 1000;
1080
1081         dhcp->timeout = g_timeout_add(delay, start_info_req, dhcp);
1082
1083         return 0;
1084 }
1085
1086 static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data)
1087 {
1088         struct connman_dhcpv6 *dhcp = user_data;
1089
1090         DBG("dhcpv6 advertise msg %p", dhcp);
1091
1092         clear_timer(dhcp);
1093
1094         g_dhcpv6_client_clear_retransmit(dhcp_client);
1095
1096         if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1097                 if (dhcp->callback != NULL)
1098                         dhcp->callback(dhcp->network, FALSE);
1099                 return;
1100         }
1101
1102         dhcp->RT = REQ_TIMEOUT * (1 + get_random());
1103         DBG("request initial RT timeout %d msec", dhcp->RT);
1104         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1105
1106         dhcp->request_count = 1;
1107
1108         dhcpv6_request(dhcp, TRUE);
1109 }
1110
1111 static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)
1112 {
1113         /* We get here if server supports rapid commit */
1114         struct connman_dhcpv6 *dhcp = user_data;
1115
1116         DBG("dhcpv6 solicitation msg %p", dhcp);
1117
1118         clear_timer(dhcp);
1119
1120         set_addresses(dhcp_client, dhcp);
1121
1122         g_dhcpv6_client_clear_retransmit(dhcp_client);
1123 }
1124
1125 static gboolean timeout_solicitation(gpointer user_data)
1126 {
1127         struct connman_dhcpv6 *dhcp = user_data;
1128
1129         dhcp->RT = calc_delay(dhcp->RT, SOL_MAX_RT);
1130
1131         DBG("solicit RT timeout %d msec", dhcp->RT);
1132
1133         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1134
1135         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1136
1137         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1138
1139         return FALSE;
1140 }
1141
1142 static int dhcpv6_solicitation(struct connman_dhcpv6 *dhcp)
1143 {
1144         struct connman_service *service;
1145         struct connman_ipconfig *ipconfig_ipv6;
1146         GDHCPClient *dhcp_client;
1147         GDHCPClientError error;
1148         int index, ret;
1149
1150         DBG("dhcp %p", dhcp);
1151
1152         index = connman_network_get_index(dhcp->network);
1153
1154         dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
1155         if (error != G_DHCP_CLIENT_ERROR_NONE) {
1156                 clear_timer(dhcp);
1157                 return -EINVAL;
1158         }
1159
1160         if (getenv("CONNMAN_DHCPV6_DEBUG"))
1161                 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
1162
1163         service = connman_service_lookup_from_network(dhcp->network);
1164         if (service == NULL) {
1165                 clear_timer(dhcp);
1166                 g_dhcp_client_unref(dhcp_client);
1167                 return -EINVAL;
1168         }
1169
1170         ret = set_duid(service, dhcp->network, dhcp_client, index);
1171         if (ret < 0) {
1172                 clear_timer(dhcp);
1173                 g_dhcp_client_unref(dhcp_client);
1174                 return ret;
1175         }
1176
1177         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1178         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
1179         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1180         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1181         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1182
1183         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1184                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1185
1186         ipconfig_ipv6 = __connman_service_get_ip6config(service);
1187         dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
1188
1189         g_dhcpv6_client_set_ia(dhcp_client, index,
1190                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1191                         NULL, NULL, FALSE, NULL);
1192
1193         clear_callbacks(dhcp_client);
1194
1195         g_dhcp_client_register_event(dhcp_client,
1196                                 G_DHCP_CLIENT_EVENT_SOLICITATION,
1197                                 solicitation_cb, dhcp);
1198
1199         g_dhcp_client_register_event(dhcp_client,
1200                                 G_DHCP_CLIENT_EVENT_ADVERTISE,
1201                                 advertise_cb, dhcp);
1202
1203         dhcp->dhcp_client = dhcp_client;
1204
1205         return g_dhcp_client_start(dhcp_client, NULL);
1206 }
1207
1208 static gboolean start_solicitation(gpointer user_data)
1209 {
1210         struct connman_dhcpv6 *dhcp = user_data;
1211
1212         /* Set the retransmission timeout, RFC 3315 chapter 14 */
1213         dhcp->RT = SOL_TIMEOUT * (1 + get_random());
1214
1215         DBG("solicit initial RT timeout %d msec", dhcp->RT);
1216
1217         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1218
1219         dhcpv6_solicitation(dhcp);
1220
1221         return FALSE;
1222 }
1223
1224 static void confirm_cb(GDHCPClient *dhcp_client, gpointer user_data)
1225 {
1226         struct connman_dhcpv6 *dhcp = user_data;
1227         int status = g_dhcpv6_client_get_status(dhcp_client);
1228
1229         DBG("dhcpv6 confirm msg %p status %d", dhcp, status);
1230
1231         clear_timer(dhcp);
1232
1233         set_addresses(dhcp_client, dhcp);
1234
1235         g_dhcpv6_client_clear_retransmit(dhcp_client);
1236
1237         /*
1238          * If confirm fails, start from scratch.
1239          */
1240         if (status != 0) {
1241                 g_dhcp_client_unref(dhcp->dhcp_client);
1242                 start_solicitation(dhcp);
1243         } else if (dhcp->callback != NULL)
1244                 dhcp->callback(dhcp->network, TRUE);
1245 }
1246
1247 static int dhcpv6_confirm(struct connman_dhcpv6 *dhcp)
1248 {
1249         GDHCPClient *dhcp_client;
1250         GDHCPClientError error;
1251         struct connman_service *service;
1252         struct connman_ipconfig *ipconfig_ipv6;
1253         int index, ret;
1254
1255         DBG("dhcp %p", dhcp);
1256
1257         index = connman_network_get_index(dhcp->network);
1258
1259         dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
1260         if (error != G_DHCP_CLIENT_ERROR_NONE) {
1261                 clear_timer(dhcp);
1262                 return -EINVAL;
1263         }
1264
1265         if (getenv("CONNMAN_DHCPV6_DEBUG"))
1266                 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
1267
1268         service = connman_service_lookup_from_network(dhcp->network);
1269         if (service == NULL) {
1270                 clear_timer(dhcp);
1271                 g_dhcp_client_unref(dhcp_client);
1272                 return -EINVAL;
1273         }
1274
1275         ret = set_duid(service, dhcp->network, dhcp_client, index);
1276         if (ret < 0) {
1277                 clear_timer(dhcp);
1278                 g_dhcp_client_unref(dhcp_client);
1279                 return ret;
1280         }
1281
1282         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1283         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
1284         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1285         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1286         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1287
1288         g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1289                                 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1290
1291         ipconfig_ipv6 = __connman_service_get_ip6config(service);
1292         dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
1293
1294         g_dhcpv6_client_set_ia(dhcp_client, index,
1295                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1296                         NULL, NULL, TRUE,
1297                         __connman_ipconfig_get_dhcp_address(ipconfig_ipv6));
1298
1299         clear_callbacks(dhcp_client);
1300
1301         g_dhcp_client_register_event(dhcp_client,
1302                                 G_DHCP_CLIENT_EVENT_CONFIRM,
1303                                 confirm_cb, dhcp);
1304
1305         dhcp->dhcp_client = dhcp_client;
1306
1307         return g_dhcp_client_start(dhcp_client, NULL);
1308 }
1309
1310 static gboolean timeout_confirm(gpointer user_data)
1311 {
1312         struct connman_dhcpv6 *dhcp = user_data;
1313
1314         dhcp->RT = calc_delay(dhcp->RT, CNF_MAX_RT);
1315
1316         DBG("confirm RT timeout %d msec", dhcp->RT);
1317
1318         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_confirm, dhcp);
1319
1320         g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1321
1322         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1323
1324         return FALSE;
1325 }
1326
1327 static gboolean timeout_max_confirm(gpointer user_data)
1328 {
1329         struct connman_dhcpv6 *dhcp = user_data;
1330
1331         dhcp->MRD = 0;
1332
1333         clear_timer(dhcp);
1334
1335         DBG("confirm max retransmit duration timeout");
1336
1337         g_dhcpv6_client_clear_retransmit(dhcp->dhcp_client);
1338
1339         if (dhcp->callback != NULL)
1340                 dhcp->callback(dhcp->network, FALSE);
1341
1342         return FALSE;
1343 }
1344
1345 static gboolean start_confirm(gpointer user_data)
1346 {
1347         struct connman_dhcpv6 *dhcp = user_data;
1348
1349         /* Set the confirm timeout, RFC 3315 chapter 14 */
1350         dhcp->RT = CNF_TIMEOUT * (1 + get_random());
1351
1352         DBG("confirm initial RT timeout %d msec", dhcp->RT);
1353
1354         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_confirm, dhcp);
1355         dhcp->MRD = g_timeout_add(CNF_MAX_RD, timeout_max_confirm, dhcp);
1356
1357         dhcpv6_confirm(dhcp);
1358
1359         return FALSE;
1360 }
1361
1362 int __connman_dhcpv6_start(struct connman_network *network,
1363                                 GSList *prefixes, dhcp_cb callback)
1364 {
1365         struct connman_service *service;
1366         struct connman_ipconfig *ipconfig_ipv6;
1367         struct connman_dhcpv6 *dhcp;
1368         char *last_address;
1369         int delay;
1370
1371         DBG("");
1372
1373         if (network_table != NULL) {
1374                 dhcp = g_hash_table_lookup(network_table, network);
1375                 if (dhcp != NULL && dhcp->started == TRUE)
1376                         return -EBUSY;
1377         }
1378
1379         service = connman_service_lookup_from_network(network);
1380         if (service == NULL)
1381                 return -EINVAL;
1382
1383         dhcp = g_try_new0(struct connman_dhcpv6, 1);
1384         if (dhcp == NULL)
1385                 return -ENOMEM;
1386
1387         dhcp->network = network;
1388         dhcp->callback = callback;
1389         dhcp->prefixes = prefixes;
1390         dhcp->started = TRUE;
1391
1392         connman_network_ref(network);
1393
1394         DBG("replace network %p dhcp %p", network, dhcp);
1395
1396         g_hash_table_replace(network_table, network, dhcp);
1397
1398         /* Initial timeout, RFC 3315, 17.1.2 */
1399         delay = rand() % 1000;
1400
1401         ipconfig_ipv6 = __connman_service_get_ip6config(service);
1402         last_address = __connman_ipconfig_get_dhcp_address(ipconfig_ipv6);
1403
1404         if (prefixes != NULL && last_address != NULL &&
1405                         check_ipv6_addr_prefix(prefixes,
1406                                                 last_address) != 128) {
1407                 /*
1408                  * So we are in the same subnet
1409                  * RFC 3315, chapter 18.1.2 Confirm message
1410                  */
1411                 dhcp->timeout = g_timeout_add(delay, start_confirm, dhcp);
1412         } else {
1413                 /*
1414                  * Start from scratch.
1415                  * RFC 3315, chapter 17.1.2 Solicitation message
1416                  */
1417                 dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
1418         }
1419
1420         return 0;
1421 }
1422
1423 void __connman_dhcpv6_stop(struct connman_network *network)
1424 {
1425         DBG("");
1426
1427         if (network_table == NULL)
1428                 return;
1429
1430         if (g_hash_table_remove(network_table, network) == TRUE)
1431                 connman_network_unref(network);
1432 }
1433
1434 int __connman_dhcpv6_init(void)
1435 {
1436         DBG("");
1437
1438         srand(time(NULL));
1439
1440         network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
1441                                                         NULL, remove_network);
1442
1443         return 0;
1444 }
1445
1446 void __connman_dhcpv6_cleanup(void)
1447 {
1448         DBG("");
1449
1450         g_hash_table_destroy(network_table);
1451         network_table = NULL;
1452 }