dhcpv6: Renew message implemented.
[framework/connectivity/connman.git] / src / dhcpv6.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2011  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
52
53 struct connman_dhcpv6 {
54         struct connman_network *network;
55         dhcp_cb callback;
56
57         char **nameservers;
58         char **timeservers;
59
60         GDHCPClient *dhcp_client;
61
62         guint timeout;          /* operation timeout in msec */
63         guint RT;               /* in msec */
64         gboolean use_ta;        /* set to TRUE if IPv6 privacy is enabled */
65         GSList *prefixes;       /* network prefixes from radvd */
66         int request_count;      /* how many times REQUEST have been sent */
67 };
68
69 static GHashTable *network_table;
70
71 static int dhcpv6_request(struct connman_dhcpv6 *dhcp, gboolean add_addresses);
72
73 static inline float get_random()
74 {
75         return (rand() % 200 - 100) / 1000.0;
76 }
77
78 /* Calculate a random delay, RFC 3315 chapter 14 */
79 /* RT and MRT are milliseconds */
80 static guint calc_delay(guint RT, guint MRT)
81 {
82         float delay = get_random();
83         float rt = RT * (2 + delay);
84
85         if (rt > MRT)
86                 rt = MRT * (1 + delay);
87
88         if (rt < 0)
89                 rt = MRT;
90
91         return (guint)rt;
92 }
93
94 static void free_prefix(gpointer data, gpointer user_data)
95 {
96         g_free(data);
97 }
98
99 static void dhcpv6_free(struct connman_dhcpv6 *dhcp)
100 {
101         g_strfreev(dhcp->nameservers);
102         g_strfreev(dhcp->timeservers);
103
104         dhcp->nameservers = NULL;
105         dhcp->timeservers = NULL;
106
107         g_slist_foreach(dhcp->prefixes, free_prefix, NULL);
108         g_slist_free(dhcp->prefixes);
109 }
110
111 static gboolean compare_string_arrays(char **array_a, char **array_b)
112 {
113         int i;
114
115         if (array_a == NULL || array_b == NULL)
116                 return FALSE;
117
118         if (g_strv_length(array_a) != g_strv_length(array_b))
119                 return FALSE;
120
121         for (i = 0; array_a[i] != NULL && array_b[i] != NULL; i++)
122                 if (g_strcmp0(array_a[i], array_b[i]) != 0)
123                         return FALSE;
124
125         return TRUE;
126 }
127
128 static void dhcpv6_debug(const char *str, void *data)
129 {
130         connman_info("%s: %s\n", (const char *) data, str);
131 }
132
133 static gchar *convert_to_hex(unsigned char *buf, int len)
134 {
135         gchar *ret = g_try_malloc(len * 2 + 1);
136         int i;
137
138         for (i = 0; ret != NULL && i < len; i++)
139                 g_snprintf(ret + i * 2, 3, "%02x", buf[i]);
140
141         return ret;
142 }
143
144 /*
145  * DUID should not change over time so save it to file.
146  * See RFC 3315 chapter 9 for details.
147  */
148 static int set_duid(struct connman_service *service,
149                         struct connman_network *network,
150                         GDHCPClient *dhcp_client, int index)
151 {
152         GKeyFile *keyfile;
153         const char *ident;
154         char *hex_duid;
155         unsigned char *duid;
156         int duid_len;
157
158         ident = __connman_service_get_ident(service);
159
160         keyfile = connman_storage_load_service(ident);
161         if (keyfile == NULL)
162                 return -EINVAL;
163
164         hex_duid = g_key_file_get_string(keyfile, ident, "IPv6.DHCP.DUID",
165                                         NULL);
166         if (hex_duid != NULL) {
167                 unsigned int i, j = 0, hex;
168                 size_t hex_duid_len = strlen(hex_duid);
169
170                 duid = g_try_malloc0(hex_duid_len / 2);
171                 if (duid == NULL) {
172                         g_key_file_free(keyfile);
173                         g_free(hex_duid);
174                         return -ENOMEM;
175                 }
176
177                 for (i = 0; i < hex_duid_len; i += 2) {
178                         sscanf(hex_duid + i, "%02x", &hex);
179                         duid[j++] = hex;
180                 }
181
182                 duid_len = hex_duid_len / 2;
183         } else {
184                 int ret;
185                 int type = __connman_ipconfig_get_type_from_index(index);
186
187                 ret = g_dhcpv6_create_duid(G_DHCPV6_DUID_LLT, index, type,
188                                         &duid, &duid_len);
189                 if (ret < 0) {
190                         g_key_file_free(keyfile);
191                         return ret;
192                 }
193
194                 hex_duid = convert_to_hex(duid, duid_len);
195                 if (hex_duid == NULL) {
196                         g_key_file_free(keyfile);
197                         return -ENOMEM;
198                 }
199
200                 g_key_file_set_string(keyfile, ident, "IPv6.DHCP.DUID",
201                                 hex_duid);
202
203                 __connman_storage_save_service(keyfile, ident);
204         }
205         g_free(hex_duid);
206
207         g_key_file_free(keyfile);
208
209         g_dhcpv6_client_set_duid(dhcp_client, duid, duid_len);
210
211         return 0;
212 }
213
214 static void clear_callbacks(GDHCPClient *dhcp_client)
215 {
216         g_dhcp_client_register_event(dhcp_client,
217                                 G_DHCP_CLIENT_EVENT_SOLICITATION,
218                                 NULL, NULL);
219
220         g_dhcp_client_register_event(dhcp_client,
221                                 G_DHCP_CLIENT_EVENT_ADVERTISE,
222                                 NULL, NULL);
223
224         g_dhcp_client_register_event(dhcp_client,
225                                 G_DHCP_CLIENT_EVENT_REQUEST,
226                                 NULL, NULL);
227
228         g_dhcp_client_register_event(dhcp_client,
229                                 G_DHCP_CLIENT_EVENT_RENEW,
230                                 NULL, NULL);
231
232         g_dhcp_client_register_event(dhcp_client,
233                                 G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
234                                 NULL, NULL);
235 }
236
237 static void info_req_cb(GDHCPClient *dhcp_client, gpointer user_data)
238 {
239         struct connman_dhcpv6 *dhcp = user_data;
240         struct connman_service *service;
241         int entries, i;
242         GList *option, *list;
243         char **nameservers, **timeservers;
244
245         DBG("dhcpv6 information-request %p", dhcp);
246
247         service = __connman_service_lookup_from_network(dhcp->network);
248         if (service == NULL) {
249                 connman_error("Can not lookup service");
250                 return;
251         }
252
253         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
254         entries = g_list_length(option);
255
256         nameservers = g_try_new0(char *, entries + 1);
257         if (nameservers != NULL) {
258                 for (i = 0, list = option; list; list = list->next, i++)
259                         nameservers[i] = g_strdup(list->data);
260         }
261
262         if (compare_string_arrays(nameservers, dhcp->nameservers) == FALSE) {
263                 if (dhcp->nameservers != NULL) {
264                         for (i = 0; dhcp->nameservers[i] != NULL; i++)
265                                 __connman_service_nameserver_remove(service,
266                                                         dhcp->nameservers[i],
267                                                         FALSE);
268                         g_strfreev(dhcp->nameservers);
269                 }
270
271                 dhcp->nameservers = nameservers;
272
273                 for (i = 0; dhcp->nameservers[i] != NULL; i++)
274                         __connman_service_nameserver_append(service,
275                                                 dhcp->nameservers[i],
276                                                 FALSE);
277         } else
278                 g_strfreev(nameservers);
279
280
281         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
282         entries = g_list_length(option);
283
284         timeservers = g_try_new0(char *, entries + 1);
285         if (timeservers != NULL) {
286                 for (i = 0, list = option; list; list = list->next, i++)
287                         timeservers[i] = g_strdup(list->data);
288         }
289
290         if (compare_string_arrays(timeservers, dhcp->timeservers) == FALSE) {
291                 if (dhcp->timeservers != NULL) {
292                         for (i = 0; dhcp->timeservers[i] != NULL; i++)
293                                 __connman_service_timeserver_remove(service,
294                                                         dhcp->timeservers[i]);
295                         g_strfreev(dhcp->timeservers);
296                 }
297
298                 dhcp->timeservers = timeservers;
299
300                 for (i = 0; dhcp->timeservers[i] != NULL; i++)
301                         __connman_service_timeserver_append(service,
302                                                         dhcp->timeservers[i]);
303         } else
304                 g_strfreev(timeservers);
305
306
307         if (dhcp->callback != NULL) {
308                 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
309                 dhcp->callback(dhcp->network, status == 0 ? TRUE : FALSE);
310         }
311 }
312
313 static int dhcpv6_info_request(struct connman_dhcpv6 *dhcp)
314 {
315         struct connman_service *service;
316         GDHCPClient *dhcp_client;
317         GDHCPClientError error;
318         int index, ret;
319
320         DBG("dhcp %p", dhcp);
321
322         index = connman_network_get_index(dhcp->network);
323
324         dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
325         if (error != G_DHCP_CLIENT_ERROR_NONE)
326                 return -EINVAL;
327
328         if (getenv("CONNMAN_DHCPV6_DEBUG"))
329                 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
330
331         service = __connman_service_lookup_from_network(dhcp->network);
332         if (service == NULL)
333                 return -EINVAL;
334
335         ret = set_duid(service, dhcp->network, dhcp_client, index);
336         if (ret < 0)
337                 return ret;
338
339         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
340         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
341         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
342
343         g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
344                                 G_DHCPV6_SNTP_SERVERS);
345
346         g_dhcp_client_register_event(dhcp_client,
347                         G_DHCP_CLIENT_EVENT_INFORMATION_REQ, info_req_cb, dhcp);
348
349         dhcp->dhcp_client = dhcp_client;
350
351         return g_dhcp_client_start(dhcp_client, NULL);
352 }
353
354 static int check_ipv6_addr_prefix(GSList *prefixes, char *address)
355 {
356         struct in6_addr addr_prefix, addr;
357         GSList *list;
358         int ret = 128, len;
359
360         for (list = prefixes; list; list = list->next) {
361                 char *prefix = list->data;
362                 const char *slash = g_strrstr(prefix, "/");
363                 const unsigned char bits[] = { 0x00, 0xFE, 0xFC, 0xF8,
364                                                 0xF0, 0xE0, 0xC0, 0x80 };
365                 int left, count, i, plen;
366
367                 if (slash == NULL)
368                         continue;
369
370                 prefix = g_strndup(prefix, slash - prefix);
371                 len = strtol(slash + 1, NULL, 10);
372                 plen = 128 - len;
373
374                 count = plen / 8;
375                 left = plen % 8;
376                 i = 16 - count;
377
378                 inet_pton(AF_INET6, prefix, &addr_prefix);
379                 inet_pton(AF_INET6, address, &addr);
380
381                 memset(&addr_prefix.s6_addr[i], 0, count);
382                 memset(&addr.s6_addr[i], 0, count);
383
384                 if (left) {
385                         addr_prefix.s6_addr[i - 1] &= bits[left];
386                         addr.s6_addr[i - 1] &= bits[left];
387                 }
388
389                 g_free(prefix);
390
391                 if (memcmp(&addr_prefix, &addr, 16) == 0) {
392                         ret = len;
393                         break;
394                 }
395         }
396
397         return ret;
398 }
399
400 static int set_addresses(GDHCPClient *dhcp_client,
401                                                 struct connman_dhcpv6 *dhcp)
402 {
403         struct connman_service *service;
404         struct connman_ipconfig *ipconfig;
405         int entries, i;
406         GList *option, *list;
407         char **nameservers, **timeservers;
408         const char *c_address;
409         char *address = NULL;
410
411         service = __connman_service_lookup_from_network(dhcp->network);
412         if (service == NULL) {
413                 connman_error("Can not lookup service");
414                 return -EINVAL;
415         }
416
417         ipconfig = __connman_service_get_ip6config(service);
418         if (ipconfig == NULL) {
419                 connman_error("Could not lookup ip6config");
420                 return -EINVAL;
421         }
422
423         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
424         entries = g_list_length(option);
425
426         nameservers = g_try_new0(char *, entries + 1);
427         if (nameservers != NULL) {
428                 for (i = 0, list = option; list; list = list->next, i++)
429                         nameservers[i] = g_strdup(list->data);
430         }
431
432         if (compare_string_arrays(nameservers, dhcp->nameservers) == FALSE) {
433                 if (dhcp->nameservers != NULL) {
434                         for (i = 0; dhcp->nameservers[i] != NULL; i++)
435                                 __connman_service_nameserver_remove(service,
436                                                         dhcp->nameservers[i],
437                                                         FALSE);
438                         g_strfreev(dhcp->nameservers);
439                 }
440
441                 dhcp->nameservers = nameservers;
442
443                 for (i = 0; dhcp->nameservers[i] != NULL; i++)
444                         __connman_service_nameserver_append(service,
445                                                         dhcp->nameservers[i],
446                                                         FALSE);
447         } else
448                 g_strfreev(nameservers);
449
450
451         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
452         entries = g_list_length(option);
453
454         timeservers = g_try_new0(char *, entries + 1);
455         if (timeservers != NULL) {
456                 for (i = 0, list = option; list; list = list->next, i++)
457                         timeservers[i] = g_strdup(list->data);
458         }
459
460         if (compare_string_arrays(timeservers, dhcp->timeservers) == FALSE) {
461                 if (dhcp->timeservers != NULL) {
462                         for (i = 0; dhcp->timeservers[i] != NULL; i++)
463                                 __connman_service_timeserver_remove(service,
464                                                         dhcp->timeservers[i]);
465                         g_strfreev(dhcp->timeservers);
466                 }
467
468                 dhcp->timeservers = timeservers;
469
470                 for (i = 0; dhcp->timeservers[i] != NULL; i++)
471                         __connman_service_timeserver_append(service,
472                                                         dhcp->timeservers[i]);
473         } else
474                 g_strfreev(timeservers);
475
476
477         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
478         if (option != NULL)
479                 address = g_strdup(option->data);
480         else {
481                 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
482                 if (option != NULL)
483                         address = g_strdup(option->data);
484         }
485
486         c_address = __connman_ipconfig_get_local(ipconfig);
487
488         if (address != NULL &&
489                         ((c_address != NULL &&
490                                 g_strcmp0(address, c_address) != 0) ||
491                         (c_address == NULL))) {
492                 int prefix_len;
493
494                 /* Is this prefix part of the subnet we are suppose to use? */
495                 prefix_len = check_ipv6_addr_prefix(dhcp->prefixes, address);
496
497                 __connman_ipconfig_set_local(ipconfig, address);
498                 __connman_ipconfig_set_prefixlen(ipconfig, prefix_len);
499
500                 DBG("new address %s/%d", address, prefix_len);
501         }
502
503         return 0;
504 }
505
506 static void re_cb(GDHCPClient *dhcp_client, gpointer user_data)
507 {
508         struct connman_dhcpv6 *dhcp = user_data;
509         uint16_t status;
510         int ret;
511
512         ret = set_addresses(dhcp_client, dhcp);
513
514         status = g_dhcpv6_client_get_status(dhcp_client);
515
516         DBG("dhcpv6 cb msg %p ret %d status %d", dhcp, ret, status);
517
518         if (ret < 0) {
519                 if (dhcp->callback != NULL)
520                         dhcp->callback(dhcp->network, FALSE);
521                 return;
522         }
523
524         if (status  == G_DHCPV6_ERROR_BINDING) {
525                 /* RFC 3315, 18.1.8 */
526                 dhcpv6_request(dhcp, FALSE);
527         } else {
528                 if (dhcp->callback != NULL)
529                         dhcp->callback(dhcp->network,
530                                                 status == 0 ? TRUE : FALSE);
531         }
532 }
533
534 static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
535 {
536         DBG("");
537
538         re_cb(dhcp_client, user_data);
539 }
540
541 static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
542                         gboolean add_addresses)
543 {
544         GDHCPClient *dhcp_client;
545         uint32_t T1, T2;
546
547         DBG("dhcp %p add %d", dhcp, add_addresses);
548
549         dhcp_client = dhcp->dhcp_client;
550
551         g_dhcp_client_clear_requests(dhcp_client);
552
553         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
554         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
555         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
556         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
557
558         g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
559                                 G_DHCPV6_SNTP_SERVERS);
560
561         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL);
562         g_dhcpv6_client_set_ia(dhcp_client,
563                         connman_network_get_index(dhcp->network),
564                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
565                         &T1, &T2, add_addresses);
566
567         clear_callbacks(dhcp_client);
568
569         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
570                                         request_cb, dhcp);
571
572         dhcp->dhcp_client = dhcp_client;
573
574         return g_dhcp_client_start(dhcp_client, NULL);
575 }
576
577 static gboolean timeout_request(gpointer user_data)
578 {
579         struct connman_dhcpv6 *dhcp = user_data;
580
581         if (dhcp->request_count >= REQ_MAX_RC) {
582                 DBG("max request retry attempts %d", dhcp->request_count);
583                 dhcp->request_count = 0;
584                 if (dhcp->callback != NULL)
585                         dhcp->callback(dhcp->network, FALSE);
586                 return FALSE;
587         }
588
589         dhcp->request_count++;
590
591         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
592         DBG("request RT timeout %d msec", dhcp->RT);
593         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
594
595         g_dhcp_client_start(dhcp->dhcp_client, NULL);
596
597         return FALSE;
598 }
599
600 static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
601 {
602         DBG("");
603
604         g_dhcpv6_client_reset_renew(dhcp_client);
605
606         re_cb(dhcp_client, user_data);
607 }
608
609 static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
610 {
611         GDHCPClient *dhcp_client;
612         uint32_t T1, T2;
613
614         DBG("dhcp %p", dhcp);
615
616         dhcp_client = dhcp->dhcp_client;
617
618         g_dhcp_client_clear_requests(dhcp_client);
619
620         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
621         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
622         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
623         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
624
625         g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
626                                 G_DHCPV6_SNTP_SERVERS);
627
628         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL);
629         g_dhcpv6_client_set_ia(dhcp_client,
630                         connman_network_get_index(dhcp->network),
631                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
632                         &T1, &T2, TRUE);
633
634         clear_callbacks(dhcp_client);
635
636         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
637                                         renew_cb, dhcp);
638
639         dhcp->dhcp_client = dhcp_client;
640
641         return g_dhcp_client_start(dhcp_client, NULL);
642 }
643
644 static gboolean timeout_renew(gpointer user_data)
645 {
646         struct connman_dhcpv6 *dhcp = user_data;
647
648         dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
649
650         DBG("renew RT timeout %d msec", dhcp->RT);
651
652         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
653
654         g_dhcp_client_start(dhcp->dhcp_client, NULL);
655
656         return FALSE;
657 }
658
659 static gboolean start_renew(gpointer user_data)
660 {
661         struct connman_dhcpv6 *dhcp = user_data;
662
663         dhcp->RT = REN_TIMEOUT * (1 + get_random());
664
665         DBG("renew initial RT timeout %d msec", dhcp->RT);
666
667         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
668
669         dhcpv6_renew(dhcp);
670
671         return FALSE;
672 }
673
674 int __connman_dhcpv6_start_renew(struct connman_network *network,
675                                                         dhcp_cb callback)
676 {
677         struct connman_dhcpv6 *dhcp;
678         uint32_t T1, T2;
679         time_t last_renew, current;
680
681         DBG("");
682
683         dhcp = g_hash_table_lookup(network_table, network);
684         if (dhcp == NULL)
685                 return -ENOENT;
686
687         if (dhcp->timeout > 0) {
688                 g_source_remove(dhcp->timeout);
689                 dhcp->timeout = 0;
690         }
691
692         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
693                                 &last_renew);
694         DBG("T1 %u T2 %u", T1, T2);
695
696         if (T1 == 0xffffffff)
697                 /* RFC 3315, 22.4 */
698                 return 0;
699
700         if (T1 == 0)
701                 /* RFC 3315, 22.4
702                  * Client can choose the timeout.
703                  */
704                 T1 = 1800;
705
706         current = time(0);
707
708         dhcp->callback = callback;
709
710         DBG("renew after %d secs", T1);
711
712         dhcp->timeout = g_timeout_add_seconds(T1, start_renew, dhcp);
713
714         return 0;
715 }
716
717 static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
718 {
719         DBG("dhcp %p", dhcp);
720
721         if (dhcp->timeout > 0) {
722                 g_source_remove(dhcp->timeout);
723                 dhcp->timeout = 0;
724         }
725
726         dhcpv6_free(dhcp);
727
728         if (dhcp->dhcp_client == NULL)
729                 return 0;
730
731         g_dhcp_client_stop(dhcp->dhcp_client);
732         g_dhcp_client_unref(dhcp->dhcp_client);
733
734         dhcp->dhcp_client = NULL;
735
736         return 0;
737 }
738
739 static void remove_network(gpointer user_data)
740 {
741         struct connman_dhcpv6 *dhcp = user_data;
742
743         DBG("dhcp %p", dhcp);
744
745         dhcpv6_release(dhcp);
746
747         g_free(dhcp);
748 }
749
750 static gboolean timeout_info_req(gpointer user_data)
751 {
752         struct connman_dhcpv6 *dhcp = user_data;
753
754         dhcp->RT = calc_delay(dhcp->RT, INF_MAX_RT);
755
756         DBG("info RT timeout %d msec", dhcp->RT);
757
758         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
759
760         g_dhcp_client_start(dhcp->dhcp_client, NULL);
761
762         return FALSE;
763 }
764
765 static gboolean start_info_req(gpointer user_data)
766 {
767         struct connman_dhcpv6 *dhcp = user_data;
768
769         /* Set the retransmission timeout, RFC 3315 chapter 14 */
770         dhcp->RT = INF_TIMEOUT * (1 + get_random());
771
772         DBG("info initial RT timeout %d msec", dhcp->RT);
773
774         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
775
776         dhcpv6_info_request(dhcp);
777
778         return FALSE;
779 }
780
781 int __connman_dhcpv6_start_info(struct connman_network *network,
782                                 dhcp_cb callback)
783 {
784         struct connman_dhcpv6 *dhcp;
785         int delay;
786
787         DBG("");
788
789         dhcp = g_try_new0(struct connman_dhcpv6, 1);
790         if (dhcp == NULL)
791                 return -ENOMEM;
792
793         dhcp->network = network;
794         dhcp->callback = callback;
795
796         connman_network_ref(network);
797
798         g_hash_table_replace(network_table, network, dhcp);
799
800         /* Initial timeout, RFC 3315, 18.1.5 */
801         delay = rand() % 1000;
802
803         dhcp->timeout = g_timeout_add(delay, start_info_req, dhcp);
804
805         return 0;
806 }
807
808 static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data)
809 {
810         struct connman_dhcpv6 *dhcp = user_data;
811
812         DBG("dhcpv6 advertise msg %p", dhcp);
813
814         if (dhcp->timeout > 0) {
815                 g_source_remove(dhcp->timeout);
816                 dhcp->timeout = 0;
817         }
818
819         if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
820                 if (dhcp->callback != NULL)
821                         dhcp->callback(dhcp->network, FALSE);
822                 return;
823         }
824
825         dhcp->RT = REQ_TIMEOUT * (1 + get_random());
826         DBG("request initial RT timeout %d msec", dhcp->RT);
827         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
828
829         dhcp->request_count = 1;
830
831         dhcpv6_request(dhcp, TRUE);
832 }
833
834 static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)
835 {
836         /* We get here if server supports rapid commit */
837         struct connman_dhcpv6 *dhcp = user_data;
838
839         DBG("dhcpv6 solicitation msg %p", dhcp);
840
841         if (dhcp->timeout > 0) {
842                 g_source_remove(dhcp->timeout);
843                 dhcp->timeout = 0;
844         }
845
846         set_addresses(dhcp_client, dhcp);
847 }
848
849 static gboolean timeout_solicitation(gpointer user_data)
850 {
851         struct connman_dhcpv6 *dhcp = user_data;
852
853         dhcp->RT = calc_delay(dhcp->RT, SOL_MAX_RT);
854
855         DBG("solicit RT timeout %d msec", dhcp->RT);
856
857         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
858
859         g_dhcp_client_start(dhcp->dhcp_client, NULL);
860
861         return FALSE;
862 }
863
864 static int dhcpv6_solicitation(struct connman_dhcpv6 *dhcp)
865 {
866         struct connman_service *service;
867         struct connman_ipconfig *ipconfig_ipv6;
868         GDHCPClient *dhcp_client;
869         GDHCPClientError error;
870         int index, ret;
871
872         DBG("dhcp %p", dhcp);
873
874         index = connman_network_get_index(dhcp->network);
875
876         dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
877         if (error != G_DHCP_CLIENT_ERROR_NONE)
878                 return -EINVAL;
879
880         if (getenv("CONNMAN_DHCPV6_DEBUG"))
881                 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
882
883         service = __connman_service_lookup_from_network(dhcp->network);
884         if (service == NULL)
885                 return -EINVAL;
886
887         ret = set_duid(service, dhcp->network, dhcp_client, index);
888         if (ret < 0)
889                 return ret;
890
891         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
892         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
893         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
894         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
895
896         g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
897                                 G_DHCPV6_SNTP_SERVERS);
898
899         ipconfig_ipv6 = __connman_service_get_ip6config(service);
900         dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
901
902         g_dhcpv6_client_set_ia(dhcp_client, index,
903                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
904                         NULL, NULL, FALSE);
905
906         clear_callbacks(dhcp_client);
907
908         g_dhcp_client_register_event(dhcp_client,
909                                 G_DHCP_CLIENT_EVENT_SOLICITATION,
910                                 solicitation_cb, dhcp);
911
912         g_dhcp_client_register_event(dhcp_client,
913                                 G_DHCP_CLIENT_EVENT_ADVERTISE,
914                                 advertise_cb, dhcp);
915
916         dhcp->dhcp_client = dhcp_client;
917
918         return g_dhcp_client_start(dhcp_client, NULL);
919 }
920
921 static gboolean start_solicitation(gpointer user_data)
922 {
923         struct connman_dhcpv6 *dhcp = user_data;
924
925         /* Set the retransmission timeout, RFC 3315 chapter 14 */
926         dhcp->RT = SOL_TIMEOUT * (1 + get_random());
927
928         DBG("solicit initial RT timeout %d msec", dhcp->RT);
929
930         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
931
932         dhcpv6_solicitation(dhcp);
933
934         return FALSE;
935 }
936
937 int __connman_dhcpv6_start(struct connman_network *network,
938                                 GSList *prefixes, dhcp_cb callback)
939 {
940         struct connman_dhcpv6 *dhcp;
941         int delay;
942
943         DBG("");
944
945         dhcp = g_try_new0(struct connman_dhcpv6, 1);
946         if (dhcp == NULL)
947                 return -ENOMEM;
948
949         dhcp->network = network;
950         dhcp->callback = callback;
951         dhcp->prefixes = prefixes;
952
953         connman_network_ref(network);
954
955         g_hash_table_replace(network_table, network, dhcp);
956
957         /* Initial timeout, RFC 3315, 17.1.2 */
958         delay = rand() % 1000;
959
960         dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
961
962         return 0;
963 }
964
965 void __connman_dhcpv6_stop(struct connman_network *network)
966 {
967         DBG("");
968
969         if (network_table == NULL)
970                 return;
971
972         if (g_hash_table_remove(network_table, network) == TRUE)
973                 connman_network_unref(network);
974 }
975
976 int __connman_dhcpv6_init(void)
977 {
978         DBG("");
979
980         srand(time(0));
981
982         network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
983                                                         NULL, remove_network);
984
985         return 0;
986 }
987
988 void __connman_dhcpv6_cleanup(void)
989 {
990         DBG("");
991
992         g_hash_table_destroy(network_table);
993         network_table = NULL;
994 }