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