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