5 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
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.
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.
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
31 #include <connman/ipconfig.h>
32 #include <connman/storage.h>
34 #include <gdhcp/gdhcp.h>
40 /* Transmission params in msec, RFC 3315 chapter 5.5 */
41 #define INF_MAX_DELAY (1 * 1000)
42 #define INF_TIMEOUT (1 * 1000)
43 #define INF_MAX_RT (120 * 1000)
44 #define SOL_MAX_DELAY (1 * 1000)
45 #define SOL_TIMEOUT (1 * 1000)
46 #define SOL_MAX_RT (120 * 1000)
47 #define REQ_TIMEOUT (1 * 1000)
48 #define REQ_MAX_RT (30 * 1000)
50 #define REN_TIMEOUT (10 * 1000)
51 #define REN_MAX_RT (600 * 1000)
52 #define REB_TIMEOUT (10 * 1000)
53 #define REB_MAX_RT (600 * 1000)
54 #define CNF_MAX_DELAY (1 * 1000)
55 #define CNF_TIMEOUT (1 * 1000)
56 #define CNF_MAX_RT (4 * 1000)
57 #define CNF_MAX_RD (10 * 1000)
58 #define DEC_TIMEOUT (1 * 1000)
67 struct connman_dhcpv6 {
68 struct connman_network *network;
74 GDHCPClient *dhcp_client;
76 guint timeout; /* operation timeout in msec */
77 guint MRD; /* max operation timeout in msec */
78 guint RT; /* in msec */
79 bool use_ta; /* set to TRUE if IPv6 privacy is enabled */
80 GSList *prefixes; /* network prefixes from radvd or dhcpv6 pd */
81 int request_count; /* how many times REQUEST have been sent */
82 bool stateless; /* TRUE if stateless DHCPv6 is used */
83 bool started; /* TRUE if we have DHCPv6 started */
86 static GHashTable *network_table;
87 static GHashTable *network_pd_table;
89 static int dhcpv6_request(struct connman_dhcpv6 *dhcp, bool add_addresses);
90 static int dhcpv6_pd_request(struct connman_dhcpv6 *dhcp);
91 static gboolean start_solicitation(gpointer user_data);
92 static int dhcpv6_renew(struct connman_dhcpv6 *dhcp);
93 static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp);
95 static void clear_timer(struct connman_dhcpv6 *dhcp)
97 if (dhcp->timeout > 0) {
98 g_source_remove(dhcp->timeout);
103 g_source_remove(dhcp->MRD);
108 static inline float get_random(void)
110 return (rand() % 200 - 100) / 1000.0;
113 /* Calculate a random delay, RFC 3315 chapter 14 */
114 /* RT and MRT are milliseconds */
115 static guint calc_delay(guint RT, guint MRT)
117 float delay = get_random();
118 float rt = RT * (2 + delay);
121 rt = MRT * (1 + delay);
129 static void free_prefix(gpointer data)
134 static void dhcpv6_free(struct connman_dhcpv6 *dhcp)
136 g_strfreev(dhcp->nameservers);
137 g_strfreev(dhcp->timeservers);
139 dhcp->nameservers = NULL;
140 dhcp->timeservers = NULL;
141 dhcp->started = false;
143 g_slist_free_full(dhcp->prefixes, free_prefix);
146 static bool compare_string_arrays(char **array_a, char **array_b)
150 if (!array_a || !array_b)
153 if (g_strv_length(array_a) != g_strv_length(array_b))
156 for (i = 0; array_a[i] && array_b[i]; i++)
157 if (g_strcmp0(array_a[i], array_b[i]) != 0)
163 static void dhcpv6_debug(const char *str, void *data)
165 connman_info("%s: %s\n", (const char *) data, str);
168 static gchar *convert_to_hex(unsigned char *buf, int len)
170 gchar *ret = g_try_malloc(len * 2 + 1);
173 for (i = 0; ret && i < len; i++)
174 g_snprintf(ret + i * 2, 3, "%02x", buf[i]);
180 * DUID should not change over time so save it to file.
181 * See RFC 3315 chapter 9 for details.
183 static int set_duid(struct connman_service *service,
184 struct connman_network *network,
185 GDHCPClient *dhcp_client, int index)
193 ident = __connman_service_get_ident(service);
195 keyfile = connman_storage_load_service(ident);
199 hex_duid = g_key_file_get_string(keyfile, ident, "IPv6.DHCP.DUID",
202 unsigned int i, j = 0, hex;
203 size_t hex_duid_len = strlen(hex_duid);
205 duid = g_try_malloc0(hex_duid_len / 2);
207 g_key_file_free(keyfile);
212 for (i = 0; i < hex_duid_len; i += 2) {
213 sscanf(hex_duid + i, "%02x", &hex);
217 duid_len = hex_duid_len / 2;
220 int type = __connman_ipconfig_get_type_from_index(index);
222 ret = g_dhcpv6_create_duid(G_DHCPV6_DUID_LLT, index, type,
225 g_key_file_free(keyfile);
229 hex_duid = convert_to_hex(duid, duid_len);
231 g_key_file_free(keyfile);
235 g_key_file_set_string(keyfile, ident, "IPv6.DHCP.DUID",
238 __connman_storage_save_service(keyfile, ident);
242 g_key_file_free(keyfile);
244 g_dhcpv6_client_set_duid(dhcp_client, duid, duid_len);
249 static void clear_callbacks(GDHCPClient *dhcp_client)
251 g_dhcp_client_register_event(dhcp_client,
252 G_DHCP_CLIENT_EVENT_SOLICITATION,
255 g_dhcp_client_register_event(dhcp_client,
256 G_DHCP_CLIENT_EVENT_ADVERTISE,
259 g_dhcp_client_register_event(dhcp_client,
260 G_DHCP_CLIENT_EVENT_REQUEST,
263 g_dhcp_client_register_event(dhcp_client,
264 G_DHCP_CLIENT_EVENT_CONFIRM,
267 g_dhcp_client_register_event(dhcp_client,
268 G_DHCP_CLIENT_EVENT_RENEW,
271 g_dhcp_client_register_event(dhcp_client,
272 G_DHCP_CLIENT_EVENT_REBIND,
275 g_dhcp_client_register_event(dhcp_client,
276 G_DHCP_CLIENT_EVENT_RELEASE,
279 g_dhcp_client_register_event(dhcp_client,
280 G_DHCP_CLIENT_EVENT_DECLINE,
283 g_dhcp_client_register_event(dhcp_client,
284 G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
288 static void info_req_cb(GDHCPClient *dhcp_client, gpointer user_data)
290 struct connman_dhcpv6 *dhcp = user_data;
291 struct connman_service *service;
293 GList *option, *list;
294 char **nameservers, **timeservers;
296 DBG("dhcpv6 information-request %p", dhcp);
298 service = connman_service_lookup_from_network(dhcp->network);
300 connman_error("Can not lookup service");
304 g_dhcpv6_client_clear_retransmit(dhcp_client);
306 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
307 entries = g_list_length(option);
309 nameservers = g_try_new0(char *, entries + 1);
311 for (i = 0, list = option; list; list = list->next, i++)
312 nameservers[i] = g_strdup(list->data);
315 if (!compare_string_arrays(nameservers, dhcp->nameservers)) {
316 if (dhcp->nameservers) {
317 for (i = 0; dhcp->nameservers[i]; i++)
318 __connman_service_nameserver_remove(service,
319 dhcp->nameservers[i],
321 g_strfreev(dhcp->nameservers);
324 dhcp->nameservers = nameservers;
326 for (i = 0; dhcp->nameservers &&
327 dhcp->nameservers[i]; i++)
328 __connman_service_nameserver_append(service,
329 dhcp->nameservers[i],
332 g_strfreev(nameservers);
335 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
336 entries = g_list_length(option);
338 timeservers = g_try_new0(char *, entries + 1);
340 for (i = 0, list = option; list; list = list->next, i++)
341 timeservers[i] = g_strdup(list->data);
344 if (!compare_string_arrays(timeservers, dhcp->timeservers)) {
345 if (dhcp->timeservers) {
346 for (i = 0; dhcp->timeservers[i]; i++)
347 __connman_service_timeserver_remove(service,
348 dhcp->timeservers[i]);
349 g_strfreev(dhcp->timeservers);
352 dhcp->timeservers = timeservers;
354 for (i = 0; dhcp->timeservers &&
355 dhcp->timeservers[i]; i++)
356 __connman_service_timeserver_append(service,
357 dhcp->timeservers[i]);
359 g_strfreev(timeservers);
362 if (dhcp->callback) {
363 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
364 dhcp->callback(dhcp->network, status == 0 ?
365 CONNMAN_DHCPV6_STATUS_SUCCEED :
366 CONNMAN_DHCPV6_STATUS_FAIL,
371 static int dhcpv6_info_request(struct connman_dhcpv6 *dhcp)
373 struct connman_service *service;
374 GDHCPClient *dhcp_client;
375 GDHCPClientError error;
378 DBG("dhcp %p", dhcp);
380 index = connman_network_get_index(dhcp->network);
382 dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
383 if (error != G_DHCP_CLIENT_ERROR_NONE) {
388 if (getenv("CONNMAN_DHCPV6_DEBUG"))
389 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
391 service = connman_service_lookup_from_network(dhcp->network);
394 g_dhcp_client_unref(dhcp_client);
398 ret = set_duid(service, dhcp->network, dhcp_client, index);
401 g_dhcp_client_unref(dhcp_client);
405 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
406 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
407 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
408 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
410 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
411 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
413 g_dhcp_client_register_event(dhcp_client,
414 G_DHCP_CLIENT_EVENT_INFORMATION_REQ, info_req_cb, dhcp);
416 dhcp->dhcp_client = dhcp_client;
418 return g_dhcp_client_start(dhcp_client, NULL);
421 static int check_ipv6_addr_prefix(GSList *prefixes, char *address)
423 struct in6_addr addr_prefix, addr;
427 for (list = prefixes; list; list = list->next) {
428 char *prefix = list->data;
429 const char *slash = g_strrstr(prefix, "/");
430 const unsigned char bits[] = { 0x00, 0xFE, 0xFC, 0xF8,
431 0xF0, 0xE0, 0xC0, 0x80 };
432 int left, count, i, plen;
437 prefix = g_strndup(prefix, slash - prefix);
438 len = strtol(slash + 1, NULL, 10);
439 if (len < 3 || len > 128)
448 inet_pton(AF_INET6, prefix, &addr_prefix);
449 inet_pton(AF_INET6, address, &addr);
451 memset(&addr_prefix.s6_addr[i], 0, count);
452 memset(&addr.s6_addr[i], 0, count);
455 addr_prefix.s6_addr[i - 1] &= bits[left];
456 addr.s6_addr[i - 1] &= bits[left];
461 if (memcmp(&addr_prefix, &addr, 16) == 0) {
470 static int set_other_addresses(GDHCPClient *dhcp_client,
471 struct connman_dhcpv6 *dhcp)
473 struct connman_service *service;
475 GList *option, *list;
476 char **nameservers, **timeservers;
478 service = connman_service_lookup_from_network(dhcp->network);
480 connman_error("Can not lookup service");
485 * Check domains before nameservers so that the nameserver append
486 * function will update domain list in service.c
488 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DOMAIN_LIST);
489 entries = g_list_length(option);
491 char **domains = g_try_new0(char *, entries + 1);
493 for (i = 0, list = option; list;
494 list = list->next, i++)
495 domains[i] = g_strdup(list->data);
496 __connman_service_update_search_domains(service, domains);
501 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
502 entries = g_list_length(option);
504 nameservers = g_try_new0(char *, entries + 1);
506 for (i = 0, list = option; list; list = list->next, i++)
507 nameservers[i] = g_strdup(list->data);
510 if (!compare_string_arrays(nameservers, dhcp->nameservers)) {
511 if (dhcp->nameservers) {
512 for (i = 0; dhcp->nameservers[i]; i++)
513 __connman_service_nameserver_remove(service,
514 dhcp->nameservers[i],
516 g_strfreev(dhcp->nameservers);
519 dhcp->nameservers = nameservers;
521 for (i = 0; dhcp->nameservers &&
522 dhcp->nameservers[i]; i++)
523 __connman_service_nameserver_append(service,
524 dhcp->nameservers[i],
527 g_strfreev(nameservers);
530 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
531 entries = g_list_length(option);
533 timeservers = g_try_new0(char *, entries + 1);
535 for (i = 0, list = option; list; list = list->next, i++)
536 timeservers[i] = g_strdup(list->data);
539 if (!compare_string_arrays(timeservers, dhcp->timeservers)) {
540 if (dhcp->timeservers) {
541 for (i = 0; dhcp->timeservers[i]; i++)
542 __connman_service_timeserver_remove(service,
543 dhcp->timeservers[i]);
544 g_strfreev(dhcp->timeservers);
547 dhcp->timeservers = timeservers;
549 for (i = 0; dhcp->timeservers &&
550 dhcp->timeservers[i]; i++)
551 __connman_service_timeserver_append(service,
552 dhcp->timeservers[i]);
554 g_strfreev(timeservers);
559 static GSList *copy_prefixes(GSList *prefixes)
561 GSList *list, *copy = NULL;
563 for (list = prefixes; list; list = list->next)
564 copy = g_slist_prepend(copy, g_strdup(list->data));
570 * Helper struct for doing DAD (duplicate address detection).
571 * It is refcounted and freed after all reply's to neighbor
572 * discovery request are received.
578 GDHCPClient *dhcp_client;
579 struct connman_ipconfig *ipconfig;
587 static void free_own_address(struct own_address *data)
589 g_dhcp_client_unref(data->dhcp_client);
590 __connman_ipconfig_unref(data->ipconfig);
591 g_slist_free_full(data->prefixes, free_prefix);
592 g_slist_free_full(data->dad_failed, g_free);
593 g_slist_free_full(data->dad_succeed, g_free);
598 static struct own_address *ref_own_address(struct own_address *address)
600 DBG("%p ref %d", address, address->refcount + 1);
602 __sync_fetch_and_add(&address->refcount, 1);
607 static void unref_own_address(struct own_address *address)
612 DBG("%p ref %d", address, address->refcount - 1);
614 if (__sync_fetch_and_sub(&address->refcount, 1) != 1)
617 free_own_address(address);
620 static void set_address(int ifindex, struct connman_ipconfig *ipconfig,
621 GSList *prefixes, char *address)
623 const char *c_address;
625 c_address = __connman_ipconfig_get_local(ipconfig);
627 if (address && ((c_address && g_strcmp0(address, c_address) != 0) ||
631 /* Is this prefix part of the subnet we are suppose to use? */
632 prefix_len = check_ipv6_addr_prefix(prefixes, address);
634 __connman_ipconfig_set_local(ipconfig, address);
635 __connman_ipconfig_set_prefixlen(ipconfig, prefix_len);
637 DBG("new address %s/%d", address, prefix_len);
639 __connman_ipconfig_set_dhcp_address(ipconfig, address);
640 __connman_service_save(
641 __connman_service_lookup_from_index(ifindex));
647 * Helper struct that is used when waiting a reply to DECLINE message.
649 struct decline_cb_data {
650 GDHCPClient *dhcp_client;
656 static void decline_reply_callback(struct decline_cb_data *data)
658 struct connman_network *network;
659 struct connman_service *service;
661 service = __connman_service_lookup_from_index(data->ifindex);
662 network = __connman_service_get_network(service);
665 data->callback(network, CONNMAN_DHCPV6_STATUS_RESTART, NULL);
667 g_dhcp_client_unref(data->dhcp_client);
670 static gboolean decline_timeout(gpointer user_data)
672 struct decline_cb_data *data = user_data;
674 DBG("ifindex %d", data->ifindex);
677 * We ignore all DECLINE replies that are received after the timeout
679 g_dhcp_client_register_event(data->dhcp_client,
680 G_DHCP_CLIENT_EVENT_DECLINE,
683 decline_reply_callback(data);
690 static void decline_cb(GDHCPClient *dhcp_client, gpointer user_data)
692 struct decline_cb_data *data = user_data;
694 DBG("ifindex %d", data->ifindex);
696 g_dhcpv6_client_clear_retransmit(dhcp_client);
699 g_source_remove(data->timeout);
701 decline_reply_callback(data);
706 static int dhcpv6_decline(GDHCPClient *dhcp_client, int ifindex,
707 dhcpv6_cb callback, GSList *failed)
709 struct decline_cb_data *data;
713 DBG("dhcp_client %p", dhcp_client);
715 g_dhcp_client_clear_requests(dhcp_client);
717 g_dhcpv6_client_clear_send(dhcp_client, G_DHCPV6_ORO);
719 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
720 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
722 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
724 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
726 code = G_DHCPV6_IA_TA;
730 code = G_DHCPV6_IA_NA;
732 g_dhcpv6_client_clear_send(dhcp_client, code);
734 g_dhcpv6_client_set_ias(dhcp_client, ifindex, code, NULL, NULL,
737 clear_callbacks(dhcp_client);
739 data = g_try_new(struct decline_cb_data, 1);
743 data->ifindex = ifindex;
744 data->callback = callback;
745 data->dhcp_client = g_dhcp_client_ref(dhcp_client);
746 data->timeout = g_timeout_add(DEC_TIMEOUT, decline_timeout, data);
748 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_DECLINE,
751 return g_dhcp_client_start(dhcp_client, NULL);
754 static void dad_reply(struct nd_neighbor_advert *reply,
755 unsigned int length, struct in6_addr *addr, void *user_data)
757 struct own_address *data = user_data;
759 char address[INET6_ADDRSTRLEN];
760 enum __connman_dhcpv6_status status = CONNMAN_DHCPV6_STATUS_FAIL;
762 inet_ntop(AF_INET6, addr, address, INET6_ADDRSTRLEN);
764 DBG("user %p reply %p len %d address %s index %d data %p", user_data,
765 reply, length, address, data->ifindex, data);
769 DBG("DAD succeed for %s", address);
771 DBG("DAD cannot be done for %s", address);
773 data->dad_succeed = g_slist_prepend(data->dad_succeed,
777 DBG("DAD failed for %s", address);
779 data->dad_failed = g_slist_prepend(data->dad_failed,
784 * If refcount == 1 then we got the final reply and can continue.
786 if (data->refcount > 1)
789 for (list = data->dad_succeed; list; list = list->next)
790 set_address(data->ifindex, data->ipconfig, data->prefixes,
793 if (data->dad_failed) {
794 dhcpv6_decline(data->dhcp_client, data->ifindex,
795 data->callback, data->dad_failed);
797 if (data->dad_succeed)
798 status = CONNMAN_DHCPV6_STATUS_SUCCEED;
800 if (data->callback) {
801 struct connman_network *network;
802 struct connman_service *service;
804 service = __connman_service_lookup_from_index(
806 network = __connman_service_get_network(service);
807 data->callback(network, status, NULL);
811 unref_own_address(data);
815 * Is the kernel configured to do DAD? If 0, then do not do DAD.
816 * See also RFC 4862 chapter 5.4 about DupAddrDetectTransmits
818 static int dad_transmits(int ifindex)
820 char name[IF_NAMESIZE];
825 if (!if_indextoname(ifindex, name))
828 path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/dad_transmits",
834 f = fopen(path, "r");
839 if (fscanf(f, "%d", &value) < 0)
848 static void do_dad(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
850 struct connman_service *service;
851 struct connman_ipconfig *ipconfig;
853 GList *option, *list;
854 struct own_address *user_data;
856 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
858 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
861 * Even if we didn't had any addresses, just try to set
864 set_other_addresses(dhcp_client, dhcp);
867 DBG("Skip DAD as no addresses found in reply");
869 dhcp->callback(dhcp->network,
870 CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
875 ifindex = connman_network_get_index(dhcp->network);
877 DBG("index %d", ifindex);
879 service = connman_service_lookup_from_network(dhcp->network);
881 connman_error("Can not lookup service for index %d", ifindex);
885 ipconfig = __connman_service_get_ip6config(service);
887 connman_error("Could not lookup ip6config for index %d",
892 if (!dad_transmits(ifindex)) {
893 DBG("Skip DAD because of kernel configuration");
895 for (list = option; list; list = list->next)
896 set_address(ifindex, ipconfig, dhcp->prefixes,
900 dhcp->callback(dhcp->network,
901 CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
906 if (g_list_length(option) == 0) {
907 DBG("No addresses when doing DAD");
909 dhcp->callback(dhcp->network,
910 CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
915 user_data = g_try_new0(struct own_address, 1);
919 user_data->refcount = 0;
920 user_data->ifindex = ifindex;
921 user_data->dhcp_client = g_dhcp_client_ref(dhcp_client);
922 user_data->ipconfig = __connman_ipconfig_ref(ipconfig);
923 user_data->prefixes = copy_prefixes(dhcp->prefixes);
924 user_data->callback = dhcp->callback;
927 * We send one neighbor discovery request / address
928 * and after all checks are done, then report the status
932 for (list = option; list; list = list->next) {
933 char *address = option->data;
934 struct in6_addr addr;
937 ref_own_address(user_data);
939 if (inet_pton(AF_INET6, address, &addr) < 0) {
940 DBG("Invalid IPv6 address %s %d/%s", address,
941 -errno, strerror(errno));
945 DBG("user %p address %s client %p ipconfig %p prefixes %p",
947 user_data->dhcp_client, user_data->ipconfig,
948 user_data->prefixes);
950 ret = __connman_inet_ipv6_do_dad(ifindex, 1000,
955 DBG("Could not send neighbor solicitation for %s",
957 dad_reply(NULL, -1, &addr, user_data);
959 DBG("Sent neighbor solicitation %d bytes", ret);
966 unref_own_address(user_data);
970 dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
974 static gboolean timeout_request_resend(gpointer user_data)
976 struct connman_dhcpv6 *dhcp = user_data;
978 if (dhcp->request_count >= REQ_MAX_RC) {
979 DBG("max request retry attempts %d", dhcp->request_count);
980 dhcp->request_count = 0;
982 dhcp->callback(dhcp->network,
983 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
987 dhcp->request_count++;
989 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
990 DBG("request resend RT timeout %d msec", dhcp->RT);
991 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request_resend, dhcp);
993 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
995 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1000 static gboolean request_resend(gpointer user_data)
1002 struct connman_dhcpv6 *dhcp = user_data;
1004 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1006 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1007 DBG("request resend RT timeout %d msec", dhcp->RT);
1008 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request_resend, dhcp);
1010 dhcpv6_request(dhcp, true);
1015 static void do_resend_request(struct connman_dhcpv6 *dhcp)
1017 if (dhcp->request_count >= REQ_MAX_RC) {
1018 DBG("max request retry attempts %d", dhcp->request_count);
1019 dhcp->request_count = 0;
1021 dhcp->callback(dhcp->network,
1022 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1026 dhcp->request_count++;
1028 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1029 DBG("resending request after %d msec", dhcp->RT);
1030 dhcp->timeout = g_timeout_add(dhcp->RT, request_resend, dhcp);
1033 static void re_cb(enum request_type req_type, GDHCPClient *dhcp_client,
1036 struct connman_dhcpv6 *dhcp = user_data;
1041 status = g_dhcpv6_client_get_status(dhcp_client);
1043 DBG("dhcpv6 cb msg %p status %d", dhcp, status);
1046 * RFC 3315, 18.1.8 handle the resend if error
1048 if (status == G_DHCPV6_ERROR_BINDING) {
1049 dhcpv6_request(dhcp, false);
1050 } else if (status == G_DHCPV6_ERROR_MCAST) {
1053 dhcpv6_request(dhcp, true);
1056 dhcpv6_rebind(dhcp);
1062 } else if (status == G_DHCPV6_ERROR_LINK) {
1063 if (req_type == REQ_REQUEST) {
1064 g_dhcp_client_unref(dhcp->dhcp_client);
1065 start_solicitation(dhcp);
1068 dhcp->callback(dhcp->network,
1069 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1071 } else if (status == G_DHCPV6_ERROR_FAILURE) {
1072 if (req_type == REQ_REQUEST) {
1073 /* Rate limit the resend of request message */
1074 do_resend_request(dhcp);
1077 dhcp->callback(dhcp->network,
1078 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1083 * If we did not got any addresses, then re-send
1088 option = g_dhcp_client_get_option(dhcp->dhcp_client,
1091 option = g_dhcp_client_get_option(dhcp->dhcp_client,
1096 dhcpv6_request(dhcp, true);
1099 dhcpv6_rebind(dhcp);
1109 if (status == G_DHCPV6_ERROR_SUCCESS)
1110 do_dad(dhcp_client, dhcp);
1111 else if (dhcp->callback)
1112 dhcp->callback(dhcp->network,
1113 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1117 static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data)
1121 g_dhcpv6_client_clear_retransmit(dhcp_client);
1123 re_cb(REQ_REBIND, dhcp_client, user_data);
1126 static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp)
1128 GDHCPClient *dhcp_client;
1130 DBG("dhcp %p", dhcp);
1132 dhcp_client = dhcp->dhcp_client;
1134 g_dhcp_client_clear_requests(dhcp_client);
1136 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1137 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1138 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1139 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1141 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1142 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1144 g_dhcpv6_client_set_ia(dhcp_client,
1145 connman_network_get_index(dhcp->network),
1146 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1147 NULL, NULL, TRUE, NULL);
1149 clear_callbacks(dhcp_client);
1151 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
1154 dhcp->dhcp_client = dhcp_client;
1156 return g_dhcp_client_start(dhcp_client, NULL);
1159 static gboolean dhcpv6_restart(gpointer user_data)
1161 struct connman_dhcpv6 *dhcp = user_data;
1164 dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
1171 * Check if we need to restart the solicitation procedure. This
1172 * is done if all the addresses have expired. RFC 3315, 18.1.4
1174 static int check_restart(struct connman_dhcpv6 *dhcp)
1176 time_t current, expired;
1178 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
1180 current = time(NULL);
1182 if (current >= expired) {
1183 DBG("expired by %d secs", (int)(current - expired));
1185 g_timeout_add(0, dhcpv6_restart, dhcp);
1193 static gboolean timeout_rebind(gpointer user_data)
1195 struct connman_dhcpv6 *dhcp = user_data;
1197 if (check_restart(dhcp) < 0)
1200 dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
1202 DBG("rebind RT timeout %d msec", dhcp->RT);
1204 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1206 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1208 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1213 static gboolean start_rebind(gpointer user_data)
1215 struct connman_dhcpv6 *dhcp = user_data;
1217 if (check_restart(dhcp) < 0)
1220 dhcp->RT = REB_TIMEOUT * (1 + get_random());
1222 DBG("rebind initial RT timeout %d msec", dhcp->RT);
1224 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1226 dhcpv6_rebind(dhcp);
1231 static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
1235 g_dhcpv6_client_reset_request(dhcp_client);
1236 g_dhcpv6_client_clear_retransmit(dhcp_client);
1238 re_cb(REQ_REQUEST, dhcp_client, user_data);
1241 static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
1244 GDHCPClient *dhcp_client;
1247 DBG("dhcp %p add %d", dhcp, add_addresses);
1249 dhcp_client = dhcp->dhcp_client;
1251 g_dhcp_client_clear_requests(dhcp_client);
1253 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1254 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1255 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1256 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1257 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1259 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1260 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1262 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1263 g_dhcpv6_client_set_ia(dhcp_client,
1264 connman_network_get_index(dhcp->network),
1265 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1266 &T1, &T2, add_addresses, NULL);
1268 clear_callbacks(dhcp_client);
1270 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
1273 dhcp->dhcp_client = dhcp_client;
1275 return g_dhcp_client_start(dhcp_client, NULL);
1278 static gboolean timeout_request(gpointer user_data)
1280 struct connman_dhcpv6 *dhcp = user_data;
1282 if (dhcp->request_count >= REQ_MAX_RC) {
1283 DBG("max request retry attempts %d", dhcp->request_count);
1284 dhcp->request_count = 0;
1286 dhcp->callback(dhcp->network,
1287 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1291 dhcp->request_count++;
1293 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1294 DBG("request RT timeout %d msec", dhcp->RT);
1295 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1297 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1299 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1304 static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
1308 g_dhcpv6_client_clear_retransmit(dhcp_client);
1310 re_cb(REQ_RENEW, dhcp_client, user_data);
1313 static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
1315 GDHCPClient *dhcp_client;
1318 DBG("dhcp %p", dhcp);
1320 dhcp_client = dhcp->dhcp_client;
1322 g_dhcp_client_clear_requests(dhcp_client);
1324 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1325 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1326 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1327 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1328 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1330 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1331 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1333 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1334 g_dhcpv6_client_set_ia(dhcp_client,
1335 connman_network_get_index(dhcp->network),
1336 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1337 &T1, &T2, TRUE, NULL);
1339 clear_callbacks(dhcp_client);
1341 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
1344 dhcp->dhcp_client = dhcp_client;
1346 return g_dhcp_client_start(dhcp_client, NULL);
1349 static gboolean timeout_renew(gpointer user_data)
1351 struct connman_dhcpv6 *dhcp = user_data;
1352 time_t last_rebind, current;
1355 if (check_restart(dhcp) < 0)
1358 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, &T2,
1359 &last_rebind, NULL);
1360 current = time(NULL);
1361 if ((unsigned)current >= (unsigned)last_rebind + T2) {
1363 * Do rebind instead if past T2
1369 dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
1371 DBG("renew RT timeout %d msec", dhcp->RT);
1373 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1375 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1377 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1382 static gboolean start_renew(gpointer user_data)
1384 struct connman_dhcpv6 *dhcp = user_data;
1386 dhcp->RT = REN_TIMEOUT * (1 + get_random());
1388 DBG("renew initial RT timeout %d msec", dhcp->RT);
1390 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1397 int __connman_dhcpv6_start_renew(struct connman_network *network,
1400 struct connman_dhcpv6 *dhcp;
1402 time_t started, current, expired;
1404 dhcp = g_hash_table_lookup(network_table, network);
1408 DBG("network %p dhcp %p", network, dhcp);
1412 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
1413 &started, &expired);
1415 current = time(NULL);
1417 DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
1418 (unsigned long)expired, current, started);
1420 if (T1 == 0xffffffff)
1421 /* RFC 3315, 22.4 */
1426 * Client can choose the timeout.
1430 dhcp->callback = callback;
1432 /* RFC 3315, 18.1.4, start solicit if expired */
1433 if (check_restart(dhcp) < 0)
1436 if (T2 != 0xffffffff && T2 > 0) {
1437 if ((unsigned)current >= (unsigned)started + T2) {
1438 /* RFC 3315, chapter 18.1.3, start rebind */
1439 DBG("rebind after %d secs", T2);
1441 dhcp->timeout = g_timeout_add_seconds(T2, start_rebind,
1444 } else if ((unsigned)current < (unsigned)started + T1) {
1445 DBG("renew after %d secs", T1);
1447 dhcp->timeout = g_timeout_add_seconds(T1, start_renew,
1450 DBG("rebind after %d secs", T2 - T1);
1452 dhcp->timeout = g_timeout_add_seconds(T2 - T1,
1461 static void release_cb(GDHCPClient *dhcp_client, gpointer user_data)
1466 int __connman_dhcpv6_start_release(struct connman_network *network,
1469 struct connman_dhcpv6 *dhcp;
1470 GDHCPClient *dhcp_client;
1473 return 0; /* we are already released */
1475 dhcp = g_hash_table_lookup(network_table, network);
1479 DBG("network %p dhcp %p client %p stateless %d", network, dhcp,
1480 dhcp->dhcp_client, dhcp->stateless);
1482 if (dhcp->stateless)
1487 dhcp_client = dhcp->dhcp_client;
1490 * We had started the DHCPv6 handshaking i.e., we have called
1491 * __connman_dhcpv6_start() but it has not yet sent
1492 * a solicitation message to server. This means that we do not
1493 * have DHCPv6 configured yet so we can just quit here.
1495 DBG("DHCPv6 was not started");
1499 g_dhcp_client_clear_requests(dhcp_client);
1500 g_dhcp_client_clear_values(dhcp_client);
1502 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1503 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1505 g_dhcpv6_client_set_ia(dhcp_client,
1506 connman_network_get_index(dhcp->network),
1507 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1508 NULL, NULL, TRUE, NULL);
1510 clear_callbacks(dhcp_client);
1513 * Although we register a callback here we are really not interested in
1514 * the answer because it might take too long time and network code
1515 * might be in the middle of the disconnect.
1516 * So we just inform the server that we are done with the addresses
1517 * but ignore the reply from server. This is allowed by RFC 3315
1520 g_dhcp_client_register_event(dhcp_client,
1521 G_DHCP_CLIENT_EVENT_RELEASE,
1524 dhcp->dhcp_client = dhcp_client;
1526 return g_dhcp_client_start(dhcp_client, NULL);
1529 static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
1531 DBG("dhcp %p", dhcp);
1537 if (!dhcp->dhcp_client)
1540 g_dhcp_client_stop(dhcp->dhcp_client);
1541 g_dhcp_client_unref(dhcp->dhcp_client);
1543 dhcp->dhcp_client = NULL;
1548 static void remove_network(gpointer user_data)
1550 struct connman_dhcpv6 *dhcp = user_data;
1552 DBG("dhcp %p", dhcp);
1554 dhcpv6_release(dhcp);
1559 static gboolean timeout_info_req(gpointer user_data)
1561 struct connman_dhcpv6 *dhcp = user_data;
1563 dhcp->RT = calc_delay(dhcp->RT, INF_MAX_RT);
1565 DBG("info RT timeout %d msec", dhcp->RT);
1567 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1569 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1571 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1576 static gboolean start_info_req(gpointer user_data)
1578 struct connman_dhcpv6 *dhcp = user_data;
1580 /* Set the retransmission timeout, RFC 3315 chapter 14 */
1581 dhcp->RT = INF_TIMEOUT * (1 + get_random());
1583 DBG("info initial RT timeout %d msec", dhcp->RT);
1585 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1587 dhcpv6_info_request(dhcp);
1592 int __connman_dhcpv6_start_info(struct connman_network *network,
1595 struct connman_dhcpv6 *dhcp;
1600 if (network_table) {
1601 dhcp = g_hash_table_lookup(network_table, network);
1602 if (dhcp && dhcp->started)
1606 dhcp = g_try_new0(struct connman_dhcpv6, 1);
1610 dhcp->network = network;
1611 dhcp->callback = callback;
1612 dhcp->stateless = true;
1613 dhcp->started = true;
1615 connman_network_ref(network);
1617 DBG("replace network %p dhcp %p", network, dhcp);
1619 g_hash_table_replace(network_table, network, dhcp);
1621 /* Initial timeout, RFC 3315, 18.1.5 */
1622 delay = rand() % 1000;
1624 dhcp->timeout = g_timeout_add(delay, start_info_req, dhcp);
1629 static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data)
1631 struct connman_dhcpv6 *dhcp = user_data;
1633 DBG("dhcpv6 advertise msg %p", dhcp);
1637 g_dhcpv6_client_clear_retransmit(dhcp_client);
1639 if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1641 dhcp->callback(dhcp->network,
1642 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1646 dhcp->RT = REQ_TIMEOUT * (1 + get_random());
1647 DBG("request initial RT timeout %d msec", dhcp->RT);
1648 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1650 dhcp->request_count = 1;
1652 dhcpv6_request(dhcp, true);
1655 static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)
1657 /* We get here if server supports rapid commit */
1658 struct connman_dhcpv6 *dhcp = user_data;
1660 DBG("dhcpv6 solicitation msg %p", dhcp);
1664 do_dad(dhcp_client, dhcp);
1666 g_dhcpv6_client_clear_retransmit(dhcp_client);
1669 static gboolean timeout_solicitation(gpointer user_data)
1671 struct connman_dhcpv6 *dhcp = user_data;
1673 dhcp->RT = calc_delay(dhcp->RT, SOL_MAX_RT);
1675 DBG("solicit RT timeout %d msec", dhcp->RT);
1677 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1679 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1681 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1686 static int dhcpv6_solicitation(struct connman_dhcpv6 *dhcp)
1688 struct connman_service *service;
1689 struct connman_ipconfig *ipconfig_ipv6;
1690 GDHCPClient *dhcp_client;
1691 GDHCPClientError error;
1694 DBG("dhcp %p", dhcp);
1696 index = connman_network_get_index(dhcp->network);
1698 dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
1699 if (error != G_DHCP_CLIENT_ERROR_NONE) {
1704 if (getenv("CONNMAN_DHCPV6_DEBUG"))
1705 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
1707 service = connman_service_lookup_from_network(dhcp->network);
1710 g_dhcp_client_unref(dhcp_client);
1714 ret = set_duid(service, dhcp->network, dhcp_client, index);
1717 g_dhcp_client_unref(dhcp_client);
1721 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1722 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
1723 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1724 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1725 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1727 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1728 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1730 ipconfig_ipv6 = __connman_service_get_ip6config(service);
1731 dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
1733 g_dhcpv6_client_set_ia(dhcp_client, index,
1734 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1735 NULL, NULL, FALSE, NULL);
1737 clear_callbacks(dhcp_client);
1739 g_dhcp_client_register_event(dhcp_client,
1740 G_DHCP_CLIENT_EVENT_SOLICITATION,
1741 solicitation_cb, dhcp);
1743 g_dhcp_client_register_event(dhcp_client,
1744 G_DHCP_CLIENT_EVENT_ADVERTISE,
1745 advertise_cb, dhcp);
1747 dhcp->dhcp_client = dhcp_client;
1749 return g_dhcp_client_start(dhcp_client, NULL);
1752 static gboolean start_solicitation(gpointer user_data)
1754 struct connman_dhcpv6 *dhcp = user_data;
1756 /* Set the retransmission timeout, RFC 3315 chapter 14 */
1757 dhcp->RT = SOL_TIMEOUT * (1 + get_random());
1759 DBG("solicit initial RT timeout %d msec", dhcp->RT);
1761 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1763 dhcpv6_solicitation(dhcp);
1768 static void confirm_cb(GDHCPClient *dhcp_client, gpointer user_data)
1770 struct connman_dhcpv6 *dhcp = user_data;
1771 int status = g_dhcpv6_client_get_status(dhcp_client);
1773 DBG("dhcpv6 confirm msg %p status %d", dhcp, status);
1777 g_dhcpv6_client_clear_retransmit(dhcp_client);
1780 * If confirm fails, start from scratch.
1783 g_dhcp_client_unref(dhcp->dhcp_client);
1784 start_solicitation(dhcp);
1786 do_dad(dhcp_client, dhcp);
1790 static int dhcpv6_confirm(struct connman_dhcpv6 *dhcp)
1792 GDHCPClient *dhcp_client;
1793 GDHCPClientError error;
1794 struct connman_service *service;
1795 struct connman_ipconfig *ipconfig_ipv6;
1798 DBG("dhcp %p", dhcp);
1800 index = connman_network_get_index(dhcp->network);
1802 dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
1803 if (error != G_DHCP_CLIENT_ERROR_NONE) {
1808 if (getenv("CONNMAN_DHCPV6_DEBUG"))
1809 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
1811 service = connman_service_lookup_from_network(dhcp->network);
1814 g_dhcp_client_unref(dhcp_client);
1818 ret = set_duid(service, dhcp->network, dhcp_client, index);
1821 g_dhcp_client_unref(dhcp_client);
1825 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1826 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
1828 ipconfig_ipv6 = __connman_service_get_ip6config(service);
1829 dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
1831 g_dhcpv6_client_set_ia(dhcp_client, index,
1832 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1834 __connman_ipconfig_get_dhcp_address(ipconfig_ipv6));
1836 clear_callbacks(dhcp_client);
1838 g_dhcp_client_register_event(dhcp_client,
1839 G_DHCP_CLIENT_EVENT_CONFIRM,
1842 dhcp->dhcp_client = dhcp_client;
1844 return g_dhcp_client_start(dhcp_client, NULL);
1847 static gboolean timeout_confirm(gpointer user_data)
1849 struct connman_dhcpv6 *dhcp = user_data;
1851 dhcp->RT = calc_delay(dhcp->RT, CNF_MAX_RT);
1853 DBG("confirm RT timeout %d msec", dhcp->RT);
1855 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_confirm, dhcp);
1857 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1859 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1864 static gboolean timeout_max_confirm(gpointer user_data)
1866 struct connman_dhcpv6 *dhcp = user_data;
1872 DBG("confirm max retransmit duration timeout");
1874 g_dhcpv6_client_clear_retransmit(dhcp->dhcp_client);
1877 dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
1883 static gboolean start_confirm(gpointer user_data)
1885 struct connman_dhcpv6 *dhcp = user_data;
1887 /* Set the confirm timeout, RFC 3315 chapter 14 */
1888 dhcp->RT = CNF_TIMEOUT * (1 + get_random());
1890 DBG("confirm initial RT timeout %d msec", dhcp->RT);
1892 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_confirm, dhcp);
1893 dhcp->MRD = g_timeout_add(CNF_MAX_RD, timeout_max_confirm, dhcp);
1895 dhcpv6_confirm(dhcp);
1900 int __connman_dhcpv6_start(struct connman_network *network,
1901 GSList *prefixes, dhcpv6_cb callback)
1903 struct connman_service *service;
1904 struct connman_ipconfig *ipconfig_ipv6;
1905 struct connman_dhcpv6 *dhcp;
1911 if (network_table) {
1912 dhcp = g_hash_table_lookup(network_table, network);
1913 if (dhcp && dhcp->started)
1917 service = connman_service_lookup_from_network(network);
1921 dhcp = g_try_new0(struct connman_dhcpv6, 1);
1925 dhcp->network = network;
1926 dhcp->callback = callback;
1927 dhcp->prefixes = prefixes;
1928 dhcp->started = true;
1930 connman_network_ref(network);
1932 DBG("replace network %p dhcp %p", network, dhcp);
1934 g_hash_table_replace(network_table, network, dhcp);
1936 /* Initial timeout, RFC 3315, 17.1.2 */
1937 delay = rand() % 1000;
1939 ipconfig_ipv6 = __connman_service_get_ip6config(service);
1940 last_address = __connman_ipconfig_get_dhcp_address(ipconfig_ipv6);
1942 if (prefixes && last_address &&
1943 check_ipv6_addr_prefix(prefixes,
1944 last_address) != 128) {
1946 * So we are in the same subnet
1947 * RFC 3315, chapter 18.1.2 Confirm message
1949 dhcp->timeout = g_timeout_add(delay, start_confirm, dhcp);
1952 * Start from scratch.
1953 * RFC 3315, chapter 17.1.2 Solicitation message
1955 dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
1961 void __connman_dhcpv6_stop(struct connman_network *network)
1968 if (g_hash_table_remove(network_table, network))
1969 connman_network_unref(network);
1972 static int save_prefixes(struct connman_ipconfig *ipconfig,
1976 int i = 0, count = g_slist_length(prefixes);
1982 array = g_try_new0(char *, count + 1);
1986 for (list = prefixes; list; list = list->next) {
1987 char *elem, addr_str[INET6_ADDRSTRLEN];
1988 GDHCPIAPrefix *prefix = list->data;
1990 elem = g_strdup_printf("%s/%d", inet_ntop(AF_INET6,
1991 &prefix->prefix, addr_str, INET6_ADDRSTRLEN),
2001 __connman_ipconfig_set_dhcpv6_prefixes(ipconfig, array);
2005 static GSList *load_prefixes(struct connman_ipconfig *ipconfig)
2008 GSList *list = NULL;
2009 char **array = __connman_ipconfig_get_dhcpv6_prefixes(ipconfig);
2014 for (i = 0; array[i]; i++) {
2015 GDHCPIAPrefix *prefix;
2017 char *ptr, **elems = g_strsplit(array[i], "/", 0);
2022 value = strtol(elems[1], &ptr, 10);
2023 if (ptr != elems[1] && *ptr == '\0' && value <= 128) {
2024 struct in6_addr addr;
2026 if (inet_pton(AF_INET6, elems[0], &addr) == 1) {
2027 prefix = g_try_new0(GDHCPIAPrefix, 1);
2032 memcpy(&prefix->prefix, &addr,
2033 sizeof(struct in6_addr));
2034 prefix->prefixlen = value;
2036 list = g_slist_prepend(list, prefix);
2046 static GDHCPIAPrefix *copy_prefix(gpointer data)
2048 GDHCPIAPrefix *copy, *prefix = data;
2050 copy = g_try_new(GDHCPIAPrefix, 1);
2054 memcpy(copy, prefix, sizeof(GDHCPIAPrefix));
2059 static GSList *copy_and_convert_prefixes(GList *prefixes)
2061 GSList *copy = NULL;
2064 for (list = prefixes; list; list = list->next)
2065 copy = g_slist_prepend(copy, copy_prefix(list->data));
2070 static int set_prefixes(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
2073 g_slist_free_full(dhcp->prefixes, free_prefix);
2076 copy_and_convert_prefixes(g_dhcp_client_get_option(dhcp_client,
2079 DBG("Got %d prefix", g_slist_length(dhcp->prefixes));
2081 if (dhcp->callback) {
2082 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
2083 if (status == G_DHCPV6_ERROR_NO_PREFIX)
2084 dhcp->callback(dhcp->network,
2085 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2087 struct connman_service *service;
2088 struct connman_ipconfig *ipconfig;
2089 int ifindex = connman_network_get_index(dhcp->network);
2091 service = __connman_service_lookup_from_index(ifindex);
2093 ipconfig = __connman_service_get_ip6config(
2095 save_prefixes(ipconfig, dhcp->prefixes);
2096 __connman_service_save(service);
2099 dhcp->callback(dhcp->network,
2100 CONNMAN_DHCPV6_STATUS_SUCCEED, dhcp->prefixes);
2103 g_slist_free_full(dhcp->prefixes, free_prefix);
2104 dhcp->prefixes = NULL;
2110 static void re_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2112 struct connman_dhcpv6 *dhcp = user_data;
2116 status = g_dhcpv6_client_get_status(dhcp_client);
2118 DBG("dhcpv6 cb msg %p status %d", dhcp, status);
2120 if (status == G_DHCPV6_ERROR_BINDING) {
2121 /* RFC 3315, 18.1.8 */
2122 dhcpv6_pd_request(dhcp);
2124 ret = set_prefixes(dhcp_client, dhcp);
2127 dhcp->callback(dhcp->network,
2128 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2134 static void rebind_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2138 g_dhcpv6_client_clear_retransmit(dhcp_client);
2140 re_pd_cb(dhcp_client, user_data);
2143 static GDHCPClient *create_pd_client(struct connman_dhcpv6 *dhcp, int *err)
2145 GDHCPClient *dhcp_client;
2146 GDHCPClientError error;
2147 struct connman_service *service;
2151 index = connman_network_get_index(dhcp->network);
2153 dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
2154 if (error != G_DHCP_CLIENT_ERROR_NONE) {
2159 if (getenv("CONNMAN_DHCPV6_DEBUG"))
2160 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6:PD");
2162 service = connman_service_lookup_from_network(dhcp->network);
2164 g_dhcp_client_unref(dhcp_client);
2169 ret = set_duid(service, dhcp->network, dhcp_client, index);
2171 g_dhcp_client_unref(dhcp_client);
2176 g_dhcpv6_client_create_iaid(dhcp_client, index, (unsigned char *)&iaid);
2177 g_dhcpv6_client_set_iaid(dhcp_client, iaid);
2182 static int dhcpv6_pd_rebind(struct connman_dhcpv6 *dhcp)
2184 GDHCPClient *dhcp_client;
2187 DBG("dhcp %p", dhcp);
2189 if (!dhcp->dhcp_client) {
2191 * We skipped the solicitation phase
2195 dhcp->dhcp_client = create_pd_client(dhcp, &err);
2196 if (!dhcp->dhcp_client) {
2202 dhcp_client = dhcp->dhcp_client;
2204 g_dhcp_client_clear_requests(dhcp_client);
2206 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2207 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2208 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2210 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2211 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2213 clear_callbacks(dhcp_client);
2215 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
2216 rebind_pd_cb, dhcp);
2218 return g_dhcp_client_start(dhcp_client, NULL);
2221 static void renew_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2225 g_dhcpv6_client_clear_retransmit(dhcp_client);
2227 re_pd_cb(dhcp_client, user_data);
2230 static int dhcpv6_pd_renew(struct connman_dhcpv6 *dhcp)
2232 GDHCPClient *dhcp_client;
2235 DBG("dhcp %p", dhcp);
2237 dhcp_client = dhcp->dhcp_client;
2239 g_dhcp_client_clear_requests(dhcp_client);
2241 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2242 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2243 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2244 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2246 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2247 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2249 clear_callbacks(dhcp_client);
2251 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
2254 return g_dhcp_client_start(dhcp_client, NULL);
2258 * Check if we need to restart the solicitation procedure. This
2259 * is done if all the prefixes have expired.
2261 static int check_pd_restart(struct connman_dhcpv6 *dhcp)
2263 time_t current, expired;
2265 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
2267 current = time(NULL);
2269 if (current > expired) {
2270 DBG("expired by %d secs", (int)(current - expired));
2272 g_timeout_add(0, dhcpv6_restart, dhcp);
2280 static gboolean timeout_pd_rebind(gpointer user_data)
2282 struct connman_dhcpv6 *dhcp = user_data;
2284 if (check_pd_restart(dhcp) < 0)
2287 dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
2289 DBG("rebind RT timeout %d msec", dhcp->RT);
2291 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2293 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2295 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2300 static gboolean start_pd_rebind(gpointer user_data)
2302 struct connman_dhcpv6 *dhcp = user_data;
2304 if (check_pd_restart(dhcp) < 0)
2307 dhcp->RT = REB_TIMEOUT * (1 + get_random());
2309 DBG("rebind initial RT timeout %d msec", dhcp->RT);
2311 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2313 dhcpv6_pd_rebind(dhcp);
2318 static gboolean timeout_pd_rebind_confirm(gpointer user_data)
2320 struct connman_dhcpv6 *dhcp = user_data;
2322 dhcp->RT = calc_delay(dhcp->RT, CNF_MAX_RT);
2324 DBG("rebind with confirm RT timeout %d msec", dhcp->RT);
2326 dhcp->timeout = g_timeout_add(dhcp->RT,
2327 timeout_pd_rebind_confirm, dhcp);
2329 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2331 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2336 static gboolean timeout_pd_max_confirm(gpointer user_data)
2338 struct connman_dhcpv6 *dhcp = user_data;
2344 DBG("rebind with confirm max retransmit duration timeout");
2346 g_dhcpv6_client_clear_retransmit(dhcp->dhcp_client);
2349 dhcp->callback(dhcp->network,
2350 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2355 static gboolean start_pd_rebind_with_confirm(gpointer user_data)
2357 struct connman_dhcpv6 *dhcp = user_data;
2359 dhcp->RT = CNF_TIMEOUT * (1 + get_random());
2361 DBG("rebind with confirm initial RT timeout %d msec", dhcp->RT);
2363 dhcp->timeout = g_timeout_add(dhcp->RT,
2364 timeout_pd_rebind_confirm, dhcp);
2365 dhcp->MRD = g_timeout_add(CNF_MAX_RD, timeout_pd_max_confirm, dhcp);
2367 dhcpv6_pd_rebind(dhcp);
2372 static gboolean timeout_pd_renew(gpointer user_data)
2374 struct connman_dhcpv6 *dhcp = user_data;
2376 if (check_pd_restart(dhcp) < 0)
2379 dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
2381 DBG("renew RT timeout %d msec", dhcp->RT);
2383 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
2385 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2387 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2392 static gboolean start_pd_renew(gpointer user_data)
2394 struct connman_dhcpv6 *dhcp = user_data;
2396 dhcp->RT = REN_TIMEOUT * (1 + get_random());
2398 DBG("renew initial RT timeout %d msec", dhcp->RT);
2400 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_renew, dhcp);
2402 dhcpv6_pd_renew(dhcp);
2407 int __connman_dhcpv6_start_pd_renew(struct connman_network *network,
2410 struct connman_dhcpv6 *dhcp;
2412 time_t started, current, expired;
2414 dhcp = g_hash_table_lookup(network_pd_table, network);
2418 DBG("network %p dhcp %p", network, dhcp);
2422 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
2423 &started, &expired);
2425 current = time(NULL);
2427 DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
2428 expired, current, started);
2430 if (T1 == 0xffffffff)
2431 /* RFC 3633, ch 9 */
2436 * Client can choose the timeout.
2440 dhcp->callback = callback;
2442 /* RFC 3315, 18.1.4, start solicit if expired */
2443 if (check_pd_restart(dhcp) < 0)
2446 if (T2 != 0xffffffff && T2 > 0) {
2447 if ((unsigned)current >= (unsigned)started + T2) {
2448 /* RFC 3315, chapter 18.1.3, start rebind */
2449 DBG("rebind after %d secs", T2);
2451 dhcp->timeout = g_timeout_add_seconds(T2,
2455 } else if ((unsigned)current < (unsigned)started + T1) {
2456 DBG("renew after %d secs", T1);
2458 dhcp->timeout = g_timeout_add_seconds(T1,
2462 DBG("rebind after %d secs", T2 - T1);
2464 dhcp->timeout = g_timeout_add_seconds(T2 - T1,
2473 static void release_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2478 int __connman_dhcpv6_start_pd_release(struct connman_network *network,
2481 struct connman_dhcpv6 *dhcp;
2482 GDHCPClient *dhcp_client;
2486 return 0; /* we are already released */
2488 dhcp = g_hash_table_lookup(network_pd_table, network);
2492 DBG("network %p dhcp %p client %p", network, dhcp, dhcp->dhcp_client);
2496 dhcp_client = dhcp->dhcp_client;
2498 DBG("DHCPv6 PD was not started");
2502 g_dhcp_client_clear_requests(dhcp_client);
2503 g_dhcp_client_clear_values(dhcp_client);
2505 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2506 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2508 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2509 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2511 clear_callbacks(dhcp_client);
2513 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RELEASE,
2514 release_pd_cb, dhcp);
2516 dhcp->dhcp_client = dhcp_client;
2518 return g_dhcp_client_start(dhcp_client, NULL);
2521 static gboolean timeout_pd_request(gpointer user_data)
2523 struct connman_dhcpv6 *dhcp = user_data;
2525 if (dhcp->request_count >= REQ_MAX_RC) {
2526 DBG("max request retry attempts %d", dhcp->request_count);
2527 dhcp->request_count = 0;
2529 dhcp->callback(dhcp->network,
2530 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2534 dhcp->request_count++;
2536 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
2537 DBG("request RT timeout %d msec", dhcp->RT);
2538 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2540 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2542 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2547 static void request_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2549 struct connman_dhcpv6 *dhcp = user_data;
2554 g_dhcpv6_client_clear_retransmit(dhcp_client);
2556 status = g_dhcpv6_client_get_status(dhcp_client);
2558 DBG("dhcpv6 pd cb msg %p status %d", dhcp, status);
2560 if (status == G_DHCPV6_ERROR_BINDING) {
2561 /* RFC 3315, 18.1.8 */
2562 dhcpv6_pd_request(dhcp);
2564 set_prefixes(dhcp_client, dhcp);
2568 static int dhcpv6_pd_request(struct connman_dhcpv6 *dhcp)
2570 GDHCPClient *dhcp_client;
2571 uint32_t T1 = 0, T2 = 0;
2573 DBG("dhcp %p", dhcp);
2575 dhcp_client = dhcp->dhcp_client;
2577 g_dhcp_client_clear_requests(dhcp_client);
2579 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2580 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2581 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2582 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2584 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2585 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2587 clear_callbacks(dhcp_client);
2589 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
2590 request_pd_cb, dhcp);
2592 return g_dhcp_client_start(dhcp_client, NULL);
2595 static void advertise_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2597 struct connman_dhcpv6 *dhcp = user_data;
2599 DBG("dhcpv6 advertise pd msg %p", dhcp);
2603 g_dhcpv6_client_clear_retransmit(dhcp_client);
2605 if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
2607 dhcp->callback(dhcp->network,
2608 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2612 dhcp->RT = REQ_TIMEOUT * (1 + get_random());
2613 DBG("request initial RT timeout %d msec", dhcp->RT);
2614 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2616 dhcp->request_count = 1;
2618 dhcpv6_pd_request(dhcp);
2621 static void solicitation_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2624 * This callback is here so that g_dhcp_client_start()
2625 * will enter the proper L3 mode.
2627 DBG("DHCPv6 %p solicitation msg received, ignoring it", user_data);
2630 static int dhcpv6_pd_solicitation(struct connman_dhcpv6 *dhcp)
2632 GDHCPClient *dhcp_client;
2635 DBG("dhcp %p", dhcp);
2637 dhcp_client = create_pd_client(dhcp, &ret);
2643 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2645 g_dhcpv6_client_set_pd(dhcp_client, NULL, NULL, NULL);
2647 clear_callbacks(dhcp_client);
2649 g_dhcp_client_register_event(dhcp_client,
2650 G_DHCP_CLIENT_EVENT_ADVERTISE,
2651 advertise_pd_cb, dhcp);
2653 g_dhcp_client_register_event(dhcp_client,
2654 G_DHCP_CLIENT_EVENT_SOLICITATION,
2655 solicitation_pd_cb, dhcp);
2657 dhcp->dhcp_client = dhcp_client;
2659 return g_dhcp_client_start(dhcp_client, NULL);
2662 static gboolean start_pd_solicitation(gpointer user_data)
2664 struct connman_dhcpv6 *dhcp = user_data;
2666 /* Set the retransmission timeout, RFC 3315 chapter 14 */
2667 dhcp->RT = SOL_TIMEOUT * (1 + get_random());
2669 DBG("solicit initial RT timeout %d msec", dhcp->RT);
2671 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
2673 dhcpv6_pd_solicitation(dhcp);
2678 int __connman_dhcpv6_start_pd(int index, GSList *prefixes, dhcpv6_cb callback)
2680 struct connman_service *service;
2681 struct connman_network *network;
2682 struct connman_dhcpv6 *dhcp;
2687 DBG("index %d", index);
2689 service = __connman_service_lookup_from_index(index);
2693 network = __connman_service_get_network(service);
2697 if (network_pd_table) {
2698 dhcp = g_hash_table_lookup(network_pd_table, network);
2699 if (dhcp && dhcp->started)
2703 dhcp = g_try_new0(struct connman_dhcpv6, 1);
2707 dhcp->network = network;
2708 dhcp->callback = callback;
2709 dhcp->started = true;
2713 * Try to load the earlier prefixes if caller did not supply
2714 * any that we could use.
2716 struct connman_ipconfig *ipconfig;
2717 ipconfig = __connman_service_get_ip6config(service);
2719 dhcp->prefixes = load_prefixes(ipconfig);
2721 dhcp->prefixes = prefixes;
2723 connman_network_ref(network);
2725 DBG("replace network %p dhcp %p", network, dhcp);
2727 g_hash_table_replace(network_pd_table, network, dhcp);
2729 if (!dhcp->prefixes) {
2731 * Refresh start, try to get prefixes.
2733 start_pd_solicitation(dhcp);
2736 * We used to have prefixes, try to use them again.
2737 * We need to use timeouts from confirm msg, RFC 3633, ch 12.1
2739 start_pd_rebind_with_confirm(dhcp);
2745 void __connman_dhcpv6_stop_pd(int index)
2747 struct connman_service *service;
2748 struct connman_network *network;
2753 DBG("index %d", index);
2755 if (!network_pd_table)
2758 service = __connman_service_lookup_from_index(index);
2762 network = __connman_service_get_network(service);
2766 __connman_dhcpv6_start_pd_release(network, NULL);
2768 if (g_hash_table_remove(network_pd_table, network))
2769 connman_network_unref(network);
2772 int __connman_dhcpv6_init(void)
2778 network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2779 NULL, remove_network);
2781 network_pd_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2782 NULL, remove_network);
2787 void __connman_dhcpv6_cleanup(void)
2791 g_hash_table_destroy(network_table);
2792 network_table = NULL;
2794 g_hash_table_destroy(network_pd_table);
2795 network_pd_table = NULL;