dhcpv6: Possible memory leak
[framework/connectivity/connman.git] / src / dhcpv6.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2012  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include <connman/ipconfig.h>
31 #include <connman/storage.h>
32
33 #include <gdhcp/gdhcp.h>
34
35 #include <glib.h>
36
37 #include "connman.h"
38
39 /* Transmission params in msec, RFC 3315 chapter 5.5 */
40 #define INF_MAX_DELAY   (1 * 1000)
41 #define INF_TIMEOUT     (1 * 1000)
42 #define INF_MAX_RT      (120 * 1000)
43 #define SOL_MAX_DELAY   (1 * 1000)
44 #define SOL_TIMEOUT     (1 * 1000)
45 #define SOL_MAX_RT      (120 * 1000)
46 #define REQ_TIMEOUT     (1 * 1000)
47 #define REQ_MAX_RT      (30 * 1000)
48 #define REQ_MAX_RC      10
49 #define REN_TIMEOUT     (10 * 1000)
50 #define REN_MAX_RT      (600 * 1000)
51 #define REB_TIMEOUT     (10 * 1000)
52 #define REB_MAX_RT      (600 * 1000)
53
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                 g_dhcp_client_unref(dhcp_client);
347                 return -EINVAL;
348         }
349
350         ret = set_duid(service, dhcp->network, dhcp_client, index);
351         if (ret < 0) {
352                 g_dhcp_client_unref(dhcp_client);
353                 return ret;
354         }
355
356         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
357         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
358         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
359
360         g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
361                                 G_DHCPV6_SNTP_SERVERS);
362
363         g_dhcp_client_register_event(dhcp_client,
364                         G_DHCP_CLIENT_EVENT_INFORMATION_REQ, info_req_cb, dhcp);
365
366         dhcp->dhcp_client = dhcp_client;
367
368         return g_dhcp_client_start(dhcp_client, NULL);
369 }
370
371 static int check_ipv6_addr_prefix(GSList *prefixes, char *address)
372 {
373         struct in6_addr addr_prefix, addr;
374         GSList *list;
375         int ret = 128, len;
376
377         for (list = prefixes; list; list = list->next) {
378                 char *prefix = list->data;
379                 const char *slash = g_strrstr(prefix, "/");
380                 const unsigned char bits[] = { 0x00, 0xFE, 0xFC, 0xF8,
381                                                 0xF0, 0xE0, 0xC0, 0x80 };
382                 int left, count, i, plen;
383
384                 if (slash == NULL)
385                         continue;
386
387                 prefix = g_strndup(prefix, slash - prefix);
388                 len = strtol(slash + 1, NULL, 10);
389                 plen = 128 - len;
390
391                 count = plen / 8;
392                 left = plen % 8;
393                 i = 16 - count;
394
395                 inet_pton(AF_INET6, prefix, &addr_prefix);
396                 inet_pton(AF_INET6, address, &addr);
397
398                 memset(&addr_prefix.s6_addr[i], 0, count);
399                 memset(&addr.s6_addr[i], 0, count);
400
401                 if (left) {
402                         addr_prefix.s6_addr[i - 1] &= bits[left];
403                         addr.s6_addr[i - 1] &= bits[left];
404                 }
405
406                 g_free(prefix);
407
408                 if (memcmp(&addr_prefix, &addr, 16) == 0) {
409                         ret = len;
410                         break;
411                 }
412         }
413
414         return ret;
415 }
416
417 static int set_addresses(GDHCPClient *dhcp_client,
418                                                 struct connman_dhcpv6 *dhcp)
419 {
420         struct connman_service *service;
421         struct connman_ipconfig *ipconfig;
422         int entries, i;
423         GList *option, *list;
424         char **nameservers, **timeservers;
425         const char *c_address;
426         char *address = NULL;
427
428         service = __connman_service_lookup_from_network(dhcp->network);
429         if (service == NULL) {
430                 connman_error("Can not lookup service");
431                 return -EINVAL;
432         }
433
434         ipconfig = __connman_service_get_ip6config(service);
435         if (ipconfig == NULL) {
436                 connman_error("Could not lookup ip6config");
437                 return -EINVAL;
438         }
439
440         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
441         entries = g_list_length(option);
442
443         nameservers = g_try_new0(char *, entries + 1);
444         if (nameservers != NULL) {
445                 for (i = 0, list = option; list; list = list->next, i++)
446                         nameservers[i] = g_strdup(list->data);
447         }
448
449         if (compare_string_arrays(nameservers, dhcp->nameservers) == FALSE) {
450                 if (dhcp->nameservers != NULL) {
451                         for (i = 0; dhcp->nameservers[i] != NULL; i++)
452                                 __connman_service_nameserver_remove(service,
453                                                         dhcp->nameservers[i],
454                                                         FALSE);
455                         g_strfreev(dhcp->nameservers);
456                 }
457
458                 dhcp->nameservers = nameservers;
459
460                 for (i = 0; dhcp->nameservers[i] != NULL; i++)
461                         __connman_service_nameserver_append(service,
462                                                         dhcp->nameservers[i],
463                                                         FALSE);
464         } else
465                 g_strfreev(nameservers);
466
467
468         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
469         entries = g_list_length(option);
470
471         timeservers = g_try_new0(char *, entries + 1);
472         if (timeservers != NULL) {
473                 for (i = 0, list = option; list; list = list->next, i++)
474                         timeservers[i] = g_strdup(list->data);
475         }
476
477         if (compare_string_arrays(timeservers, dhcp->timeservers) == FALSE) {
478                 if (dhcp->timeservers != NULL) {
479                         for (i = 0; dhcp->timeservers[i] != NULL; i++)
480                                 __connman_service_timeserver_remove(service,
481                                                         dhcp->timeservers[i]);
482                         g_strfreev(dhcp->timeservers);
483                 }
484
485                 dhcp->timeservers = timeservers;
486
487                 for (i = 0; dhcp->timeservers[i] != NULL; i++)
488                         __connman_service_timeserver_append(service,
489                                                         dhcp->timeservers[i]);
490         } else
491                 g_strfreev(timeservers);
492
493
494         option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
495         if (option != NULL)
496                 address = g_strdup(option->data);
497         else {
498                 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
499                 if (option != NULL)
500                         address = g_strdup(option->data);
501         }
502
503         c_address = __connman_ipconfig_get_local(ipconfig);
504
505         if (address != NULL &&
506                         ((c_address != NULL &&
507                                 g_strcmp0(address, c_address) != 0) ||
508                         (c_address == NULL))) {
509                 int prefix_len;
510
511                 /* Is this prefix part of the subnet we are suppose to use? */
512                 prefix_len = check_ipv6_addr_prefix(dhcp->prefixes, address);
513
514                 __connman_ipconfig_set_local(ipconfig, address);
515                 __connman_ipconfig_set_prefixlen(ipconfig, prefix_len);
516
517                 DBG("new address %s/%d", address, prefix_len);
518         }
519
520         return 0;
521 }
522
523 static void re_cb(GDHCPClient *dhcp_client, gpointer user_data)
524 {
525         struct connman_dhcpv6 *dhcp = user_data;
526         uint16_t status;
527         int ret;
528
529         ret = set_addresses(dhcp_client, dhcp);
530
531         status = g_dhcpv6_client_get_status(dhcp_client);
532
533         DBG("dhcpv6 cb msg %p ret %d status %d", dhcp, ret, status);
534
535         if (ret < 0) {
536                 if (dhcp->callback != NULL)
537                         dhcp->callback(dhcp->network, FALSE);
538                 return;
539         }
540
541         if (status  == G_DHCPV6_ERROR_BINDING) {
542                 /* RFC 3315, 18.1.8 */
543                 dhcpv6_request(dhcp, FALSE);
544         } else {
545                 if (dhcp->callback != NULL)
546                         dhcp->callback(dhcp->network,
547                                                 status == 0 ? TRUE : FALSE);
548         }
549 }
550
551 static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data)
552 {
553         DBG("");
554
555         g_dhcpv6_client_reset_rebind(dhcp_client);
556         g_dhcpv6_client_reset_renew(dhcp_client);
557
558         re_cb(dhcp_client, user_data);
559 }
560
561 static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp)
562 {
563         GDHCPClient *dhcp_client;
564
565         DBG("dhcp %p", dhcp);
566
567         dhcp_client = dhcp->dhcp_client;
568
569         g_dhcp_client_clear_requests(dhcp_client);
570
571         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
572         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
573         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
574
575         g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
576                                 G_DHCPV6_SNTP_SERVERS);
577
578         g_dhcpv6_client_set_ia(dhcp_client,
579                         connman_network_get_index(dhcp->network),
580                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
581                         NULL, NULL, FALSE);
582
583         clear_callbacks(dhcp_client);
584
585         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
586                                         rebind_cb, dhcp);
587
588         dhcp->dhcp_client = dhcp_client;
589
590         return g_dhcp_client_start(dhcp_client, NULL);
591 }
592
593 static gboolean dhcpv6_restart(gpointer user_data)
594 {
595         struct connman_dhcpv6 *dhcp = user_data;
596
597         if (dhcp->callback != NULL)
598                 dhcp->callback(dhcp->network, FALSE);
599
600         return FALSE;
601 }
602
603 /*
604  * Check if we need to restart the solicitation procedure. This
605  * is done if all the addresses have expired. RFC 3315, 18.1.4
606  */
607 static int check_restart(struct connman_dhcpv6 *dhcp)
608 {
609         time_t current, expired;
610
611         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
612                                 NULL, NULL, &expired);
613         current = time(NULL);
614
615         if (current > expired) {
616                 DBG("expired by %d secs", (int)(current - expired));
617
618                 g_timeout_add(0, dhcpv6_restart, dhcp);
619
620                 return -ETIMEDOUT;
621         }
622
623         return 0;
624 }
625
626 static gboolean timeout_rebind(gpointer user_data)
627 {
628         struct connman_dhcpv6 *dhcp = user_data;
629
630         if (check_restart(dhcp) < 0)
631                 return FALSE;
632
633         dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
634
635         DBG("rebind RT timeout %d msec", dhcp->RT);
636
637         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
638
639         g_dhcp_client_start(dhcp->dhcp_client, NULL);
640
641         return FALSE;
642 }
643
644 static gboolean start_rebind(gpointer user_data)
645 {
646         struct connman_dhcpv6 *dhcp = user_data;
647
648         dhcp->RT = REB_TIMEOUT * (1 + get_random());
649
650         DBG("rebind initial RT timeout %d msec", dhcp->RT);
651
652         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
653
654         dhcpv6_rebind(dhcp);
655
656         return FALSE;
657 }
658
659 static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
660 {
661         DBG("");
662
663         re_cb(dhcp_client, user_data);
664 }
665
666 static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
667                         gboolean add_addresses)
668 {
669         GDHCPClient *dhcp_client;
670         uint32_t T1, T2;
671
672         DBG("dhcp %p add %d", dhcp, add_addresses);
673
674         dhcp_client = dhcp->dhcp_client;
675
676         g_dhcp_client_clear_requests(dhcp_client);
677
678         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
679         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
680         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
681         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
682
683         g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
684                                 G_DHCPV6_SNTP_SERVERS);
685
686         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL, NULL);
687         g_dhcpv6_client_set_ia(dhcp_client,
688                         connman_network_get_index(dhcp->network),
689                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
690                         &T1, &T2, add_addresses);
691
692         clear_callbacks(dhcp_client);
693
694         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
695                                         request_cb, dhcp);
696
697         dhcp->dhcp_client = dhcp_client;
698
699         return g_dhcp_client_start(dhcp_client, NULL);
700 }
701
702 static gboolean timeout_request(gpointer user_data)
703 {
704         struct connman_dhcpv6 *dhcp = user_data;
705
706         if (dhcp->request_count >= REQ_MAX_RC) {
707                 DBG("max request retry attempts %d", dhcp->request_count);
708                 dhcp->request_count = 0;
709                 if (dhcp->callback != NULL)
710                         dhcp->callback(dhcp->network, FALSE);
711                 return FALSE;
712         }
713
714         dhcp->request_count++;
715
716         dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
717         DBG("request RT timeout %d msec", dhcp->RT);
718         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
719
720         g_dhcp_client_start(dhcp->dhcp_client, NULL);
721
722         return FALSE;
723 }
724
725 static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
726 {
727         DBG("");
728
729         g_dhcpv6_client_reset_renew(dhcp_client);
730
731         re_cb(dhcp_client, user_data);
732 }
733
734 static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
735 {
736         GDHCPClient *dhcp_client;
737         uint32_t T1, T2;
738
739         DBG("dhcp %p", dhcp);
740
741         dhcp_client = dhcp->dhcp_client;
742
743         g_dhcp_client_clear_requests(dhcp_client);
744
745         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
746         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
747         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
748         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
749
750         g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
751                                 G_DHCPV6_SNTP_SERVERS);
752
753         g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL, NULL);
754         g_dhcpv6_client_set_ia(dhcp_client,
755                         connman_network_get_index(dhcp->network),
756                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
757                         &T1, &T2, TRUE);
758
759         clear_callbacks(dhcp_client);
760
761         g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
762                                         renew_cb, dhcp);
763
764         dhcp->dhcp_client = dhcp_client;
765
766         return g_dhcp_client_start(dhcp_client, NULL);
767 }
768
769 static gboolean timeout_renew(gpointer user_data)
770 {
771         struct connman_dhcpv6 *dhcp = user_data;
772
773         if (check_restart(dhcp) < 0)
774                 return FALSE;
775
776         dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
777
778         DBG("renew RT timeout %d msec", dhcp->RT);
779
780         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
781
782         g_dhcp_client_start(dhcp->dhcp_client, NULL);
783
784         return FALSE;
785 }
786
787 static gboolean start_renew(gpointer user_data)
788 {
789         struct connman_dhcpv6 *dhcp = user_data;
790
791         dhcp->RT = REN_TIMEOUT * (1 + get_random());
792
793         DBG("renew initial RT timeout %d msec", dhcp->RT);
794
795         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
796
797         dhcpv6_renew(dhcp);
798
799         return FALSE;
800 }
801
802 int __connman_dhcpv6_start_renew(struct connman_network *network,
803                                                         dhcp_cb callback)
804 {
805         struct connman_dhcpv6 *dhcp;
806         uint32_t T1, T2;
807         time_t last_renew, last_rebind, current, expired;
808
809         dhcp = g_hash_table_lookup(network_table, network);
810         if (dhcp == NULL)
811                 return -ENOENT;
812
813         DBG("network %p dhcp %p", network, dhcp);
814
815         if (dhcp->timeout > 0) {
816                 g_source_remove(dhcp->timeout);
817                 dhcp->timeout = 0;
818         }
819
820         g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
821                                 &last_renew, &last_rebind, &expired);
822
823         current = time(NULL);
824
825         DBG("T1 %u T2 %u expires %lu current %lu", T1, T2,
826                 (unsigned long)expired, current);
827
828         if (T1 == 0xffffffff)
829                 /* RFC 3315, 22.4 */
830                 return 0;
831
832         if (T1 == 0)
833                 /* RFC 3315, 22.4
834                  * Client can choose the timeout.
835                  */
836                 T1 = 1800;
837
838         /* RFC 3315, 18.1.4, start solicit if expired */
839         if (current > expired) {
840                 DBG("expired by %d secs", (int)(current - expired));
841                 return -ETIMEDOUT;
842         }
843
844         dhcp->callback = callback;
845
846         if (T2 != 0xffffffff && T2 > 0 &&
847                         (unsigned)current > (unsigned)last_rebind + T2) {
848                 int timeout;
849
850                 /* RFC 3315, chapter 18.1.3, start rebind */
851                 if ((unsigned)current > (unsigned)last_renew + T1)
852                         timeout = 0;
853                 else
854                         timeout = last_renew - current + T1;
855
856                 /*
857                  * If we just did a renew, do not restart the rebind
858                  * immediately.
859                  */
860                 dhcp->timeout = g_timeout_add_seconds(timeout, start_rebind,
861                                                 dhcp);
862         } else {
863                 DBG("renew after %d secs", T1);
864
865                 dhcp->timeout = g_timeout_add_seconds(T1, start_renew, dhcp);
866         }
867         return 0;
868 }
869
870 int __connman_dhcpv6_start_release(struct connman_network *network,
871                                 dhcp_cb callback)
872 {
873         struct connman_dhcpv6 *dhcp;
874         GDHCPClient *dhcp_client;
875
876         if (network_table == NULL)
877                 return 0;   /* we are already released */
878
879         dhcp = g_hash_table_lookup(network_table, network);
880         if (dhcp == NULL)
881                 return -ENOENT;
882
883         DBG("network %p dhcp %p client %p stateless %d", network, dhcp,
884                                         dhcp->dhcp_client, dhcp->stateless);
885
886         if (dhcp->stateless == TRUE)
887                 return -EINVAL;
888
889         if (dhcp->timeout > 0) {
890                 g_source_remove(dhcp->timeout);
891                 dhcp->timeout = 0;
892         }
893
894         dhcp_client = dhcp->dhcp_client;
895         if (dhcp_client == NULL) {
896                 /*
897                  * We had started the DHCPv6 handshaking i.e., we have called
898                  * __connman_dhcpv6_start() but it has not yet sent
899                  * a solicitation message to server. This means that we do not
900                  * have DHCPv6 configured yet so we can just quit here.
901                  */
902                 DBG("DHCPv6 was not started");
903                 return 0;
904         }
905
906         g_dhcp_client_clear_requests(dhcp_client);
907         g_dhcp_client_clear_values(dhcp_client);
908
909         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
910         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
911
912         g_dhcpv6_client_set_ia(dhcp_client,
913                         connman_network_get_index(dhcp->network),
914                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
915                         NULL, NULL, TRUE);
916
917         clear_callbacks(dhcp_client);
918
919         /*
920          * We do not register callback here because the answer might take too
921          * long time and network code might be in the middle of the disconnect.
922          * So we just inform the server that we are done with the addresses
923          * but ignore the reply from server. This is allowed by RFC 3315
924          * chapter 18.1.6.
925          */
926
927         dhcp->dhcp_client = dhcp_client;
928
929         return g_dhcp_client_start(dhcp_client, NULL);
930 }
931
932 static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
933 {
934         DBG("dhcp %p", dhcp);
935
936         if (dhcp->timeout > 0) {
937                 g_source_remove(dhcp->timeout);
938                 dhcp->timeout = 0;
939         }
940
941         dhcpv6_free(dhcp);
942
943         if (dhcp->dhcp_client == NULL)
944                 return 0;
945
946         g_dhcp_client_stop(dhcp->dhcp_client);
947         g_dhcp_client_unref(dhcp->dhcp_client);
948
949         dhcp->dhcp_client = NULL;
950
951         return 0;
952 }
953
954 static void remove_network(gpointer user_data)
955 {
956         struct connman_dhcpv6 *dhcp = user_data;
957
958         DBG("dhcp %p", dhcp);
959
960         dhcpv6_release(dhcp);
961
962         g_free(dhcp);
963 }
964
965 static gboolean timeout_info_req(gpointer user_data)
966 {
967         struct connman_dhcpv6 *dhcp = user_data;
968
969         dhcp->RT = calc_delay(dhcp->RT, INF_MAX_RT);
970
971         DBG("info RT timeout %d msec", dhcp->RT);
972
973         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
974
975         g_dhcp_client_start(dhcp->dhcp_client, NULL);
976
977         return FALSE;
978 }
979
980 static gboolean start_info_req(gpointer user_data)
981 {
982         struct connman_dhcpv6 *dhcp = user_data;
983
984         /* Set the retransmission timeout, RFC 3315 chapter 14 */
985         dhcp->RT = INF_TIMEOUT * (1 + get_random());
986
987         DBG("info initial RT timeout %d msec", dhcp->RT);
988
989         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
990
991         dhcpv6_info_request(dhcp);
992
993         return FALSE;
994 }
995
996 int __connman_dhcpv6_start_info(struct connman_network *network,
997                                 dhcp_cb callback)
998 {
999         struct connman_dhcpv6 *dhcp;
1000         int delay;
1001
1002         DBG("");
1003
1004         if (network_table != NULL) {
1005                 dhcp = g_hash_table_lookup(network_table, network);
1006                 if (dhcp != NULL && dhcp->started == TRUE)
1007                         return -EBUSY;
1008         }
1009
1010         dhcp = g_try_new0(struct connman_dhcpv6, 1);
1011         if (dhcp == NULL)
1012                 return -ENOMEM;
1013
1014         dhcp->network = network;
1015         dhcp->callback = callback;
1016         dhcp->stateless = TRUE;
1017         dhcp->started = TRUE;
1018
1019         connman_network_ref(network);
1020
1021         DBG("replace network %p dhcp %p", network, dhcp);
1022
1023         g_hash_table_replace(network_table, network, dhcp);
1024
1025         /* Initial timeout, RFC 3315, 18.1.5 */
1026         delay = rand() % 1000;
1027
1028         dhcp->timeout = g_timeout_add(delay, start_info_req, dhcp);
1029
1030         return 0;
1031 }
1032
1033 static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data)
1034 {
1035         struct connman_dhcpv6 *dhcp = user_data;
1036
1037         DBG("dhcpv6 advertise msg %p", dhcp);
1038
1039         if (dhcp->timeout > 0) {
1040                 g_source_remove(dhcp->timeout);
1041                 dhcp->timeout = 0;
1042         }
1043
1044         if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1045                 if (dhcp->callback != NULL)
1046                         dhcp->callback(dhcp->network, FALSE);
1047                 return;
1048         }
1049
1050         dhcp->RT = REQ_TIMEOUT * (1 + get_random());
1051         DBG("request initial RT timeout %d msec", dhcp->RT);
1052         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1053
1054         dhcp->request_count = 1;
1055
1056         dhcpv6_request(dhcp, TRUE);
1057 }
1058
1059 static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)
1060 {
1061         /* We get here if server supports rapid commit */
1062         struct connman_dhcpv6 *dhcp = user_data;
1063
1064         DBG("dhcpv6 solicitation msg %p", dhcp);
1065
1066         if (dhcp->timeout > 0) {
1067                 g_source_remove(dhcp->timeout);
1068                 dhcp->timeout = 0;
1069         }
1070
1071         set_addresses(dhcp_client, dhcp);
1072 }
1073
1074 static gboolean timeout_solicitation(gpointer user_data)
1075 {
1076         struct connman_dhcpv6 *dhcp = user_data;
1077
1078         dhcp->RT = calc_delay(dhcp->RT, SOL_MAX_RT);
1079
1080         DBG("solicit RT timeout %d msec", dhcp->RT);
1081
1082         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1083
1084         g_dhcp_client_start(dhcp->dhcp_client, NULL);
1085
1086         return FALSE;
1087 }
1088
1089 static int dhcpv6_solicitation(struct connman_dhcpv6 *dhcp)
1090 {
1091         struct connman_service *service;
1092         struct connman_ipconfig *ipconfig_ipv6;
1093         GDHCPClient *dhcp_client;
1094         GDHCPClientError error;
1095         int index, ret;
1096
1097         DBG("dhcp %p", dhcp);
1098
1099         index = connman_network_get_index(dhcp->network);
1100
1101         dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
1102         if (error != G_DHCP_CLIENT_ERROR_NONE)
1103                 return -EINVAL;
1104
1105         if (getenv("CONNMAN_DHCPV6_DEBUG"))
1106                 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
1107
1108         service = __connman_service_lookup_from_network(dhcp->network);
1109         if (service == NULL) {
1110                 g_dhcp_client_unref(dhcp_client);
1111                 return -EINVAL;
1112         }
1113
1114         ret = set_duid(service, dhcp->network, dhcp_client, index);
1115         if (ret < 0) {
1116                 g_dhcp_client_unref(dhcp_client);
1117                 return ret;
1118         }
1119
1120         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1121         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
1122         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1123         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1124
1125         g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
1126                                 G_DHCPV6_SNTP_SERVERS);
1127
1128         ipconfig_ipv6 = __connman_service_get_ip6config(service);
1129         dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
1130
1131         g_dhcpv6_client_set_ia(dhcp_client, index,
1132                         dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1133                         NULL, NULL, FALSE);
1134
1135         clear_callbacks(dhcp_client);
1136
1137         g_dhcp_client_register_event(dhcp_client,
1138                                 G_DHCP_CLIENT_EVENT_SOLICITATION,
1139                                 solicitation_cb, dhcp);
1140
1141         g_dhcp_client_register_event(dhcp_client,
1142                                 G_DHCP_CLIENT_EVENT_ADVERTISE,
1143                                 advertise_cb, dhcp);
1144
1145         dhcp->dhcp_client = dhcp_client;
1146
1147         return g_dhcp_client_start(dhcp_client, NULL);
1148 }
1149
1150 static gboolean start_solicitation(gpointer user_data)
1151 {
1152         struct connman_dhcpv6 *dhcp = user_data;
1153
1154         /* Set the retransmission timeout, RFC 3315 chapter 14 */
1155         dhcp->RT = SOL_TIMEOUT * (1 + get_random());
1156
1157         DBG("solicit initial RT timeout %d msec", dhcp->RT);
1158
1159         dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1160
1161         dhcpv6_solicitation(dhcp);
1162
1163         return FALSE;
1164 }
1165
1166 int __connman_dhcpv6_start(struct connman_network *network,
1167                                 GSList *prefixes, dhcp_cb callback)
1168 {
1169         struct connman_dhcpv6 *dhcp;
1170         int delay;
1171
1172         DBG("");
1173
1174         if (network_table != NULL) {
1175                 dhcp = g_hash_table_lookup(network_table, network);
1176                 if (dhcp != NULL && dhcp->started == TRUE)
1177                         return -EBUSY;
1178         }
1179
1180         dhcp = g_try_new0(struct connman_dhcpv6, 1);
1181         if (dhcp == NULL)
1182                 return -ENOMEM;
1183
1184         dhcp->network = network;
1185         dhcp->callback = callback;
1186         dhcp->prefixes = prefixes;
1187         dhcp->started = TRUE;
1188
1189         connman_network_ref(network);
1190
1191         DBG("replace network %p dhcp %p", network, dhcp);
1192
1193         g_hash_table_replace(network_table, network, dhcp);
1194
1195         /* Initial timeout, RFC 3315, 17.1.2 */
1196         delay = rand() % 1000;
1197
1198         dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
1199
1200         return 0;
1201 }
1202
1203 void __connman_dhcpv6_stop(struct connman_network *network)
1204 {
1205         DBG("");
1206
1207         if (network_table == NULL)
1208                 return;
1209
1210         if (g_hash_table_remove(network_table, network) == TRUE)
1211                 connman_network_unref(network);
1212 }
1213
1214 int __connman_dhcpv6_init(void)
1215 {
1216         DBG("");
1217
1218         srand(time(NULL));
1219
1220         network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
1221                                                         NULL, remove_network);
1222
1223         return 0;
1224 }
1225
1226 void __connman_dhcpv6_cleanup(void)
1227 {
1228         DBG("");
1229
1230         g_hash_table_destroy(network_table);
1231         network_table = NULL;
1232 }