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 guint get_random(void)
112 __connman_util_get_random(&val);
114 /* Make sure the value is always positive so strip MSB */
115 return ((uint32_t)val) >> 1;
118 static guint compute_random(guint val)
120 return val - val / 10 +
121 (get_random() % (2 * 1000)) * val / 10 / 1000;
124 /* Calculate a random delay, RFC 3315 chapter 14 */
125 /* RT and MRT are milliseconds */
126 static guint calc_delay(guint RT, guint MRT)
128 if (MRT && (RT > MRT / 2))
129 RT = compute_random(MRT);
131 RT += compute_random(RT);
136 static guint initial_rt(guint timeout)
138 return compute_random(timeout);
141 static void free_prefix(gpointer data)
146 static void dhcpv6_free(struct connman_dhcpv6 *dhcp)
148 g_strfreev(dhcp->nameservers);
149 g_strfreev(dhcp->timeservers);
151 dhcp->nameservers = NULL;
152 dhcp->timeservers = NULL;
153 dhcp->started = false;
155 g_slist_free_full(dhcp->prefixes, free_prefix);
158 static bool compare_string_arrays(char **array_a, char **array_b)
162 if (!array_a || !array_b)
165 if (g_strv_length(array_a) != g_strv_length(array_b))
168 for (i = 0; array_a[i] && array_b[i]; i++)
169 if (g_strcmp0(array_a[i], array_b[i]) != 0)
175 static void dhcpv6_debug(const char *str, void *data)
177 connman_info("%s: %s\n", (const char *) data, str);
180 static gchar *convert_to_hex(unsigned char *buf, int len)
182 gchar *ret = g_try_malloc(len * 2 + 1);
185 for (i = 0; ret && i < len; i++)
186 g_snprintf(ret + i * 2, 3, "%02x", buf[i]);
192 * DUID should not change over time so save it to file.
193 * See RFC 3315 chapter 9 for details.
195 static int set_duid(struct connman_service *service,
196 struct connman_network *network,
197 GDHCPClient *dhcp_client, int index)
205 ident = __connman_service_get_ident(service);
207 keyfile = connman_storage_load_service(ident);
211 hex_duid = g_key_file_get_string(keyfile, ident, "IPv6.DHCP.DUID",
214 unsigned int i, j = 0, hex;
215 size_t hex_duid_len = strlen(hex_duid);
217 duid = g_try_malloc0(hex_duid_len / 2);
219 g_key_file_free(keyfile);
224 for (i = 0; i < hex_duid_len; i += 2) {
225 sscanf(hex_duid + i, "%02x", &hex);
229 duid_len = hex_duid_len / 2;
232 int type = __connman_ipconfig_get_type_from_index(index);
234 ret = g_dhcpv6_create_duid(G_DHCPV6_DUID_LLT, index, type,
237 g_key_file_free(keyfile);
241 hex_duid = convert_to_hex(duid, duid_len);
243 g_key_file_free(keyfile);
247 g_key_file_set_string(keyfile, ident, "IPv6.DHCP.DUID",
250 __connman_storage_save_service(keyfile, ident);
254 g_key_file_free(keyfile);
256 g_dhcpv6_client_set_duid(dhcp_client, duid, duid_len);
261 static void clear_callbacks(GDHCPClient *dhcp_client)
263 g_dhcp_client_register_event(dhcp_client,
264 G_DHCP_CLIENT_EVENT_SOLICITATION,
267 g_dhcp_client_register_event(dhcp_client,
268 G_DHCP_CLIENT_EVENT_ADVERTISE,
271 g_dhcp_client_register_event(dhcp_client,
272 G_DHCP_CLIENT_EVENT_REQUEST,
275 g_dhcp_client_register_event(dhcp_client,
276 G_DHCP_CLIENT_EVENT_CONFIRM,
279 g_dhcp_client_register_event(dhcp_client,
280 G_DHCP_CLIENT_EVENT_RENEW,
283 g_dhcp_client_register_event(dhcp_client,
284 G_DHCP_CLIENT_EVENT_REBIND,
287 g_dhcp_client_register_event(dhcp_client,
288 G_DHCP_CLIENT_EVENT_RELEASE,
291 g_dhcp_client_register_event(dhcp_client,
292 G_DHCP_CLIENT_EVENT_DECLINE,
295 g_dhcp_client_register_event(dhcp_client,
296 G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
300 static void info_req_cb(GDHCPClient *dhcp_client, gpointer user_data)
302 struct connman_dhcpv6 *dhcp = user_data;
303 struct connman_service *service;
305 GList *option, *list;
306 char **nameservers, **timeservers;
308 DBG("dhcpv6 information-request %p", dhcp);
310 service = connman_service_lookup_from_network(dhcp->network);
312 connman_error("Can not lookup service");
316 g_dhcpv6_client_clear_retransmit(dhcp_client);
318 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
319 entries = g_list_length(option);
321 nameservers = g_try_new0(char *, entries + 1);
323 for (i = 0, list = option; list; list = list->next, i++)
324 nameservers[i] = g_strdup(list->data);
327 if (!compare_string_arrays(nameservers, dhcp->nameservers)) {
328 if (dhcp->nameservers) {
329 for (i = 0; dhcp->nameservers[i]; i++)
330 #if defined TIZEN_EXT
332 __connman_service_nameserver_remove(service,
333 dhcp->nameservers[i], false,
334 CONNMAN_IPCONFIG_TYPE_IPV6);
336 __connman_service_nameserver_remove(service,
337 dhcp->nameservers[i],
340 #if defined TIZEN_EXT
343 g_strfreev(dhcp->nameservers);
346 dhcp->nameservers = nameservers;
348 for (i = 0; dhcp->nameservers &&
349 dhcp->nameservers[i]; i++)
350 #if defined TIZEN_EXT
352 __connman_service_nameserver_append(service,
353 dhcp->nameservers[i], false,
354 CONNMAN_IPCONFIG_TYPE_IPV6);
356 __connman_service_nameserver_append(service,
357 dhcp->nameservers[i],
360 #if defined TIZEN_EXT
364 g_strfreev(nameservers);
367 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
368 entries = g_list_length(option);
370 timeservers = g_try_new0(char *, entries + 1);
372 for (i = 0, list = option; list; list = list->next, i++)
373 timeservers[i] = g_strdup(list->data);
376 if (!compare_string_arrays(timeservers, dhcp->timeservers)) {
377 if (dhcp->timeservers) {
378 for (i = 0; dhcp->timeservers[i]; i++)
379 __connman_service_timeserver_remove(service,
380 dhcp->timeservers[i]);
381 g_strfreev(dhcp->timeservers);
384 dhcp->timeservers = timeservers;
386 for (i = 0; dhcp->timeservers &&
387 dhcp->timeservers[i]; i++)
388 __connman_service_timeserver_append(service,
389 dhcp->timeservers[i]);
391 g_strfreev(timeservers);
394 if (dhcp->callback) {
395 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
396 dhcp->callback(dhcp->network, status == 0 ?
397 CONNMAN_DHCPV6_STATUS_SUCCEED :
398 CONNMAN_DHCPV6_STATUS_FAIL,
403 static int dhcpv6_info_request(struct connman_dhcpv6 *dhcp)
405 struct connman_service *service;
406 GDHCPClient *dhcp_client;
407 GDHCPClientError error;
410 DBG("dhcp %p", dhcp);
412 index = connman_network_get_index(dhcp->network);
414 dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
415 if (error != G_DHCP_CLIENT_ERROR_NONE) {
420 #if !defined TIZEN_EXT
421 if (getenv("CONNMAN_DHCPV6_DEBUG"))
423 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
425 service = connman_service_lookup_from_network(dhcp->network);
428 g_dhcp_client_unref(dhcp_client);
432 ret = set_duid(service, dhcp->network, dhcp_client, index);
435 g_dhcp_client_unref(dhcp_client);
439 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
440 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
441 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
442 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
444 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
445 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
447 g_dhcp_client_register_event(dhcp_client,
448 G_DHCP_CLIENT_EVENT_INFORMATION_REQ, info_req_cb, dhcp);
450 dhcp->dhcp_client = dhcp_client;
452 return g_dhcp_client_start(dhcp_client, NULL);
455 static int check_ipv6_addr_prefix(GSList *prefixes, char *address)
457 struct in6_addr addr_prefix, addr;
461 for (list = prefixes; list; list = list->next) {
462 char *prefix = list->data;
463 const char *slash = g_strrstr(prefix, "/");
464 const unsigned char bits[] = { 0x00, 0xFE, 0xFC, 0xF8,
465 0xF0, 0xE0, 0xC0, 0x80 };
466 int left, count, i, plen;
471 prefix = g_strndup(prefix, slash - prefix);
472 len = strtol(slash + 1, NULL, 10);
473 if (len < 3 || len > 128)
482 inet_pton(AF_INET6, prefix, &addr_prefix);
483 inet_pton(AF_INET6, address, &addr);
485 memset(&addr_prefix.s6_addr[i], 0, count);
486 memset(&addr.s6_addr[i], 0, count);
489 addr_prefix.s6_addr[i - 1] &= bits[left];
490 addr.s6_addr[i - 1] &= bits[left];
495 if (memcmp(&addr_prefix, &addr, 16) == 0) {
504 static int set_other_addresses(GDHCPClient *dhcp_client,
505 struct connman_dhcpv6 *dhcp)
507 struct connman_service *service;
509 GList *option, *list;
510 char **nameservers, **timeservers;
512 service = connman_service_lookup_from_network(dhcp->network);
514 connman_error("Can not lookup service");
519 * Check domains before nameservers so that the nameserver append
520 * function will update domain list in service.c
522 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DOMAIN_LIST);
523 entries = g_list_length(option);
525 char **domains = g_try_new0(char *, entries + 1);
527 for (i = 0, list = option; list;
528 list = list->next, i++)
529 domains[i] = g_strdup(list->data);
530 __connman_service_set_search_domains(service, domains);
535 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
536 entries = g_list_length(option);
538 nameservers = g_try_new0(char *, entries + 1);
540 for (i = 0, list = option; list; list = list->next, i++)
541 nameservers[i] = g_strdup(list->data);
544 if (!compare_string_arrays(nameservers, dhcp->nameservers)) {
545 if (dhcp->nameservers) {
546 for (i = 0; dhcp->nameservers[i]; i++)
547 #if defined TIZEN_EXT
549 __connman_service_nameserver_remove(service,
550 dhcp->nameservers[i],
551 false, CONNMAN_IPCONFIG_TYPE_IPV6);
553 __connman_service_nameserver_remove(service,
554 dhcp->nameservers[i],
557 #if defined TIZEN_EXT
560 g_strfreev(dhcp->nameservers);
563 dhcp->nameservers = nameservers;
565 for (i = 0; dhcp->nameservers &&
566 dhcp->nameservers[i]; i++)
567 #if defined TIZEN_EXT
569 __connman_service_nameserver_append(service,
570 dhcp->nameservers[i],
571 false, CONNMAN_IPCONFIG_TYPE_IPV6);
573 __connman_service_nameserver_append(service,
574 dhcp->nameservers[i],
577 #if defined TIZEN_EXT
581 g_strfreev(nameservers);
584 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
585 entries = g_list_length(option);
587 timeservers = g_try_new0(char *, entries + 1);
589 for (i = 0, list = option; list; list = list->next, i++)
590 timeservers[i] = g_strdup(list->data);
593 if (!compare_string_arrays(timeservers, dhcp->timeservers)) {
594 if (dhcp->timeservers) {
595 for (i = 0; dhcp->timeservers[i]; i++)
596 __connman_service_timeserver_remove(service,
597 dhcp->timeservers[i]);
598 g_strfreev(dhcp->timeservers);
601 dhcp->timeservers = timeservers;
603 for (i = 0; dhcp->timeservers &&
604 dhcp->timeservers[i]; i++)
605 __connman_service_timeserver_append(service,
606 dhcp->timeservers[i]);
608 g_strfreev(timeservers);
613 static GSList *copy_prefixes(GSList *prefixes)
615 GSList *list, *copy = NULL;
617 for (list = prefixes; list; list = list->next)
618 copy = g_slist_prepend(copy, g_strdup(list->data));
624 * Helper struct for doing DAD (duplicate address detection).
625 * It is refcounted and freed after all reply's to neighbor
626 * discovery request are received.
632 GDHCPClient *dhcp_client;
633 struct connman_ipconfig *ipconfig;
641 static void free_own_address(struct own_address *data)
643 g_dhcp_client_unref(data->dhcp_client);
644 __connman_ipconfig_unref(data->ipconfig);
645 g_slist_free_full(data->prefixes, free_prefix);
646 g_slist_free_full(data->dad_failed, g_free);
647 g_slist_free_full(data->dad_succeed, g_free);
652 static struct own_address *ref_own_address(struct own_address *address)
654 DBG("%p ref %d", address, address->refcount + 1);
656 __sync_fetch_and_add(&address->refcount, 1);
661 static void unref_own_address(struct own_address *address)
666 DBG("%p ref %d", address, address->refcount - 1);
668 if (__sync_fetch_and_sub(&address->refcount, 1) != 1)
671 free_own_address(address);
674 static void set_address(int ifindex, struct connman_ipconfig *ipconfig,
675 GSList *prefixes, char *address)
677 const char *c_address;
679 c_address = __connman_ipconfig_get_local(ipconfig);
681 if (address && ((c_address && g_strcmp0(address, c_address) != 0) ||
685 /* Is this prefix part of the subnet we are suppose to use? */
686 prefix_len = check_ipv6_addr_prefix(prefixes, address);
688 #if defined TIZEN_EXT
689 char *gateway = g_strdup(__connman_ipconfig_get_gateway(ipconfig));
691 __connman_ipconfig_address_remove(ipconfig);
692 __connman_ipconfig_set_local(ipconfig, address);
693 __connman_ipconfig_set_prefixlen(ipconfig, prefix_len);
695 DBG("new address %s/%d", address, prefix_len);
697 __connman_ipconfig_set_dhcp_address(ipconfig, address);
698 #if defined TIZEN_EXT
699 DBG("Set gateway %s", gateway);
700 __connman_ipconfig_set_gateway(ipconfig, gateway);
703 __connman_service_save(
704 __connman_service_lookup_from_index(ifindex));
710 * Helper struct that is used when waiting a reply to DECLINE message.
712 struct decline_cb_data {
713 GDHCPClient *dhcp_client;
719 static void decline_reply_callback(struct decline_cb_data *data)
721 struct connman_network *network;
722 struct connman_service *service;
724 service = __connman_service_lookup_from_index(data->ifindex);
725 network = __connman_service_get_network(service);
728 data->callback(network, CONNMAN_DHCPV6_STATUS_RESTART, NULL);
730 g_dhcp_client_unref(data->dhcp_client);
733 static gboolean decline_timeout(gpointer user_data)
735 struct decline_cb_data *data = user_data;
737 DBG("ifindex %d", data->ifindex);
740 * We ignore all DECLINE replies that are received after the timeout
742 g_dhcp_client_register_event(data->dhcp_client,
743 G_DHCP_CLIENT_EVENT_DECLINE,
746 decline_reply_callback(data);
753 static void decline_cb(GDHCPClient *dhcp_client, gpointer user_data)
755 struct decline_cb_data *data = user_data;
757 DBG("ifindex %d", data->ifindex);
759 g_dhcpv6_client_clear_retransmit(dhcp_client);
762 g_source_remove(data->timeout);
764 decline_reply_callback(data);
769 static int dhcpv6_decline(GDHCPClient *dhcp_client, int ifindex,
770 dhcpv6_cb callback, GSList *failed)
772 struct decline_cb_data *data;
776 DBG("dhcp_client %p", dhcp_client);
778 g_dhcp_client_clear_requests(dhcp_client);
780 g_dhcpv6_client_clear_send(dhcp_client, G_DHCPV6_ORO);
782 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
783 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
785 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
787 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
789 code = G_DHCPV6_IA_TA;
793 code = G_DHCPV6_IA_NA;
795 g_dhcpv6_client_clear_send(dhcp_client, code);
797 g_dhcpv6_client_set_ias(dhcp_client, ifindex, code, NULL, NULL,
800 clear_callbacks(dhcp_client);
802 data = g_try_new(struct decline_cb_data, 1);
806 data->ifindex = ifindex;
807 data->callback = callback;
808 data->dhcp_client = g_dhcp_client_ref(dhcp_client);
809 data->timeout = g_timeout_add(DEC_TIMEOUT, decline_timeout, data);
811 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_DECLINE,
814 return g_dhcp_client_start(dhcp_client, NULL);
817 static void dad_reply(struct nd_neighbor_advert *reply,
818 unsigned int length, struct in6_addr *addr, void *user_data)
820 struct own_address *data = user_data;
822 char address[INET6_ADDRSTRLEN];
823 enum __connman_dhcpv6_status status = CONNMAN_DHCPV6_STATUS_FAIL;
825 inet_ntop(AF_INET6, addr, address, INET6_ADDRSTRLEN);
827 DBG("user %p reply %p len %d address %s index %d data %p", user_data,
828 reply, length, address, data->ifindex, data);
832 DBG("DAD succeed for %s", address);
834 DBG("DAD cannot be done for %s", address);
836 data->dad_succeed = g_slist_prepend(data->dad_succeed,
840 DBG("DAD failed for %s", address);
842 data->dad_failed = g_slist_prepend(data->dad_failed,
847 * If refcount == 1 then we got the final reply and can continue.
849 if (data->refcount > 1)
852 for (list = data->dad_succeed; list; list = list->next)
853 set_address(data->ifindex, data->ipconfig, data->prefixes,
856 if (data->dad_failed) {
857 dhcpv6_decline(data->dhcp_client, data->ifindex,
858 data->callback, data->dad_failed);
860 if (data->dad_succeed)
861 status = CONNMAN_DHCPV6_STATUS_SUCCEED;
863 if (data->callback) {
864 struct connman_network *network;
865 struct connman_service *service;
867 service = __connman_service_lookup_from_index(
869 network = __connman_service_get_network(service);
871 data->callback(network, status, NULL);
875 unref_own_address(data);
879 * Is the kernel configured to do DAD? If 0, then do not do DAD.
880 * See also RFC 4862 chapter 5.4 about DupAddrDetectTransmits
882 static int dad_transmits(int ifindex)
884 char name[IF_NAMESIZE];
889 if (!if_indextoname(ifindex, name))
892 path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/dad_transmits",
898 f = fopen(path, "r");
903 if (fscanf(f, "%d", &value) < 0)
912 static void do_dad(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
914 struct connman_service *service;
915 struct connman_ipconfig *ipconfig;
917 GList *option, *list;
918 struct own_address *user_data;
920 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
922 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
925 * Even if we didn't had any addresses, just try to set
928 set_other_addresses(dhcp_client, dhcp);
931 DBG("Skip DAD as no addresses found in reply");
933 dhcp->callback(dhcp->network,
934 CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
939 ifindex = connman_network_get_index(dhcp->network);
941 DBG("index %d", ifindex);
943 service = connman_service_lookup_from_network(dhcp->network);
945 connman_error("Can not lookup service for index %d", ifindex);
949 ipconfig = __connman_service_get_ip6config(service);
951 connman_error("Could not lookup ip6config for index %d",
956 if (!dad_transmits(ifindex)) {
957 DBG("Skip DAD because of kernel configuration");
959 for (list = option; list; list = list->next)
960 set_address(ifindex, ipconfig, dhcp->prefixes,
964 dhcp->callback(dhcp->network,
965 CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
970 if (g_list_length(option) == 0) {
971 DBG("No addresses when doing DAD");
973 dhcp->callback(dhcp->network,
974 CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
979 user_data = g_try_new0(struct own_address, 1);
983 user_data->refcount = 0;
984 user_data->ifindex = ifindex;
985 user_data->dhcp_client = g_dhcp_client_ref(dhcp_client);
986 user_data->ipconfig = __connman_ipconfig_ref(ipconfig);
987 user_data->prefixes = copy_prefixes(dhcp->prefixes);
988 user_data->callback = dhcp->callback;
991 * We send one neighbor discovery request / address
992 * and after all checks are done, then report the status
996 for (list = option; list; list = list->next) {
997 char *address = option->data;
998 struct in6_addr addr;
1001 ref_own_address(user_data);
1003 if (inet_pton(AF_INET6, address, &addr) < 0) {
1004 DBG("Invalid IPv6 address %s %d/%s", address,
1005 -errno, strerror(errno));
1009 DBG("user %p address %s client %p ipconfig %p prefixes %p",
1011 user_data->dhcp_client, user_data->ipconfig,
1012 user_data->prefixes);
1014 ret = __connman_inet_ipv6_do_dad(ifindex, 1000,
1019 DBG("Could not send neighbor solicitation for %s",
1021 dad_reply(NULL, -1, &addr, user_data);
1023 DBG("Sent neighbor solicitation %d bytes", ret);
1030 unref_own_address(user_data);
1034 dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
1038 static gboolean timeout_request_resend(gpointer user_data)
1040 struct connman_dhcpv6 *dhcp = user_data;
1042 if (dhcp->request_count >= REQ_MAX_RC) {
1043 DBG("max request retry attempts %d", dhcp->request_count);
1044 dhcp->request_count = 0;
1046 dhcp->callback(dhcp->network,
1047 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1051 dhcp->request_count++;
1053 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1054 DBG("request resend RT timeout %d msec", dhcp->RT);
1055 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request_resend, dhcp);
1057 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1059 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1064 static gboolean request_resend(gpointer user_data)
1066 struct connman_dhcpv6 *dhcp = user_data;
1068 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1070 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1071 DBG("request resend RT timeout %d msec", dhcp->RT);
1072 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request_resend, dhcp);
1074 dhcpv6_request(dhcp, true);
1079 static void do_resend_request(struct connman_dhcpv6 *dhcp)
1081 if (dhcp->request_count >= REQ_MAX_RC) {
1082 DBG("max request retry attempts %d", dhcp->request_count);
1083 dhcp->request_count = 0;
1085 dhcp->callback(dhcp->network,
1086 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1090 dhcp->request_count++;
1092 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1093 DBG("resending request after %d msec", dhcp->RT);
1094 dhcp->timeout = g_timeout_add(dhcp->RT, request_resend, dhcp);
1097 static void re_cb(enum request_type req_type, GDHCPClient *dhcp_client,
1100 struct connman_dhcpv6 *dhcp = user_data;
1105 status = g_dhcpv6_client_get_status(dhcp_client);
1107 DBG("dhcpv6 cb msg %p status %d", dhcp, status);
1110 * RFC 3315, 18.1.8 handle the resend if error
1112 if (status == G_DHCPV6_ERROR_BINDING) {
1113 dhcpv6_request(dhcp, false);
1114 } else if (status == G_DHCPV6_ERROR_MCAST) {
1117 dhcpv6_request(dhcp, true);
1120 dhcpv6_rebind(dhcp);
1126 } else if (status == G_DHCPV6_ERROR_LINK) {
1127 if (req_type == REQ_REQUEST) {
1128 g_dhcp_client_unref(dhcp->dhcp_client);
1129 start_solicitation(dhcp);
1132 dhcp->callback(dhcp->network,
1133 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1135 } else if (status == G_DHCPV6_ERROR_FAILURE) {
1136 if (req_type == REQ_REQUEST) {
1137 /* Rate limit the resend of request message */
1138 do_resend_request(dhcp);
1141 dhcp->callback(dhcp->network,
1142 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1147 * If we did not got any addresses, then re-send
1152 option = g_dhcp_client_get_option(dhcp->dhcp_client,
1155 option = g_dhcp_client_get_option(dhcp->dhcp_client,
1160 do_resend_request(dhcp);
1163 dhcpv6_rebind(dhcp);
1173 if (status == G_DHCPV6_ERROR_SUCCESS)
1174 do_dad(dhcp_client, dhcp);
1175 else if (dhcp->callback)
1176 dhcp->callback(dhcp->network,
1177 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1181 static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data)
1185 g_dhcpv6_client_reset_request(dhcp_client);
1186 g_dhcpv6_client_clear_retransmit(dhcp_client);
1188 re_cb(REQ_REBIND, dhcp_client, user_data);
1191 static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp)
1193 GDHCPClient *dhcp_client;
1195 DBG("dhcp %p", dhcp);
1197 dhcp_client = dhcp->dhcp_client;
1199 g_dhcp_client_clear_requests(dhcp_client);
1201 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1202 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1203 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1204 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1206 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1207 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1209 g_dhcpv6_client_set_ia(dhcp_client,
1210 connman_network_get_index(dhcp->network),
1211 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1212 NULL, NULL, TRUE, NULL);
1214 clear_callbacks(dhcp_client);
1216 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
1219 dhcp->dhcp_client = dhcp_client;
1221 return g_dhcp_client_start(dhcp_client, NULL);
1224 static gboolean dhcpv6_restart(gpointer user_data)
1226 struct connman_dhcpv6 *dhcp = user_data;
1229 dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
1236 * Check if we need to restart the solicitation procedure. This
1237 * is done if all the addresses have expired. RFC 3315, 18.1.4
1239 static int check_restart(struct connman_dhcpv6 *dhcp)
1241 time_t current, expired;
1243 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
1246 /* infinite lifetime for an DHCPv6 address */
1247 if (expired == 0xffffffff)
1250 current = time(NULL);
1252 if (current >= expired) {
1253 DBG("expired by %d secs", (int)(current - expired));
1255 g_idle_add(dhcpv6_restart, dhcp);
1263 static gboolean timeout_rebind(gpointer user_data)
1265 struct connman_dhcpv6 *dhcp = user_data;
1267 if (check_restart(dhcp) < 0)
1270 dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
1272 DBG("rebind RT timeout %d msec", dhcp->RT);
1274 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1276 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1278 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1283 static gboolean start_rebind(gpointer user_data)
1285 struct connman_dhcpv6 *dhcp = user_data;
1287 if (check_restart(dhcp) < 0)
1290 dhcp->RT = initial_rt(REB_TIMEOUT);
1292 DBG("rebind initial RT timeout %d msec", dhcp->RT);
1294 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1296 dhcpv6_rebind(dhcp);
1301 static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
1305 g_dhcpv6_client_reset_request(dhcp_client);
1306 g_dhcpv6_client_clear_retransmit(dhcp_client);
1308 re_cb(REQ_REQUEST, dhcp_client, user_data);
1311 static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
1314 GDHCPClient *dhcp_client;
1317 DBG("dhcp %p add %d", dhcp, add_addresses);
1319 dhcp_client = dhcp->dhcp_client;
1321 g_dhcp_client_clear_requests(dhcp_client);
1323 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1324 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1325 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1326 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1327 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1329 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1330 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1332 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1333 g_dhcpv6_client_set_ia(dhcp_client,
1334 connman_network_get_index(dhcp->network),
1335 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1336 &T1, &T2, add_addresses, NULL);
1338 clear_callbacks(dhcp_client);
1340 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
1343 dhcp->dhcp_client = dhcp_client;
1345 return g_dhcp_client_start(dhcp_client, NULL);
1348 static gboolean timeout_request(gpointer user_data)
1350 struct connman_dhcpv6 *dhcp = user_data;
1352 if (dhcp->request_count >= REQ_MAX_RC) {
1353 DBG("max request retry attempts %d", dhcp->request_count);
1354 dhcp->request_count = 0;
1356 dhcp->callback(dhcp->network,
1357 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1361 dhcp->request_count++;
1363 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1364 DBG("request RT timeout %d msec", dhcp->RT);
1365 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1367 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1369 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1374 static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
1378 g_dhcpv6_client_reset_request(dhcp_client);
1379 g_dhcpv6_client_clear_retransmit(dhcp_client);
1381 re_cb(REQ_RENEW, dhcp_client, user_data);
1384 static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
1386 GDHCPClient *dhcp_client;
1389 DBG("dhcp %p", dhcp);
1391 dhcp_client = dhcp->dhcp_client;
1393 g_dhcp_client_clear_requests(dhcp_client);
1395 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1396 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1397 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1398 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1399 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1401 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1402 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1404 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1405 g_dhcpv6_client_set_ia(dhcp_client,
1406 connman_network_get_index(dhcp->network),
1407 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1408 &T1, &T2, TRUE, NULL);
1410 clear_callbacks(dhcp_client);
1412 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
1415 dhcp->dhcp_client = dhcp_client;
1417 return g_dhcp_client_start(dhcp_client, NULL);
1420 static gboolean timeout_renew(gpointer user_data)
1422 struct connman_dhcpv6 *dhcp = user_data;
1423 time_t last_rebind, current;
1426 if (check_restart(dhcp) < 0)
1429 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, &T2,
1430 &last_rebind, NULL);
1431 current = time(NULL);
1432 if ((unsigned)current >= (unsigned)last_rebind + T2) {
1434 * Do rebind instead if past T2
1440 dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
1442 DBG("renew RT timeout %d msec", dhcp->RT);
1444 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1446 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1448 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1453 static gboolean start_renew(gpointer user_data)
1455 struct connman_dhcpv6 *dhcp = user_data;
1457 dhcp->RT = initial_rt(REN_TIMEOUT);
1459 DBG("renew initial RT timeout %d msec", dhcp->RT);
1461 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1468 int __connman_dhcpv6_start_renew(struct connman_network *network,
1471 struct connman_dhcpv6 *dhcp;
1472 uint32_t T1, T2, delta;
1473 time_t started, current, expired;
1475 dhcp = g_hash_table_lookup(network_table, network);
1479 DBG("network %p dhcp %p", network, dhcp);
1483 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
1484 &started, &expired);
1486 current = time(NULL);
1488 DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
1489 (unsigned long)expired, current, started);
1491 if (T1 == 0xffffffff)
1492 /* RFC 3315, 22.4 */
1497 * Client can choose the timeout.
1499 T1 = (expired - started) / 2;
1500 T2 = (expired - started) / 10 * 8;
1503 dhcp->callback = callback;
1505 /* RFC 3315, 18.1.4, start solicit if expired */
1506 if (check_restart(dhcp) < 0)
1509 if (T2 != 0xffffffff && T2 > 0) {
1510 if ((unsigned)current >= (unsigned)started + T2) {
1511 /* RFC 3315, chapter 18.1.3, start rebind */
1512 DBG("start rebind immediately");
1514 dhcp->timeout = g_idle_add(start_rebind, dhcp);
1516 } else if ((unsigned)current < (unsigned)started + T1) {
1517 delta = started + T1 - current;
1518 DBG("renew after %d secs", delta);
1520 dhcp->timeout = g_timeout_add_seconds(delta,
1523 delta = started + T2 - current;
1524 DBG("rebind after %d secs", delta);
1526 dhcp->timeout = g_timeout_add_seconds(delta,
1527 start_rebind, dhcp);
1534 static void release_cb(GDHCPClient *dhcp_client, gpointer user_data)
1539 int __connman_dhcpv6_start_release(struct connman_network *network,
1542 struct connman_dhcpv6 *dhcp;
1543 GDHCPClient *dhcp_client;
1546 return 0; /* we are already released */
1548 dhcp = g_hash_table_lookup(network_table, network);
1552 DBG("network %p dhcp %p client %p stateless %d", network, dhcp,
1553 dhcp->dhcp_client, dhcp->stateless);
1555 if (dhcp->stateless)
1560 dhcp_client = dhcp->dhcp_client;
1563 * We had started the DHCPv6 handshaking i.e., we have called
1564 * __connman_dhcpv6_start() but it has not yet sent
1565 * a solicitation message to server. This means that we do not
1566 * have DHCPv6 configured yet so we can just quit here.
1568 DBG("DHCPv6 was not started");
1572 g_dhcp_client_clear_requests(dhcp_client);
1573 g_dhcp_client_clear_values(dhcp_client);
1575 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1576 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1578 g_dhcpv6_client_set_ia(dhcp_client,
1579 connman_network_get_index(dhcp->network),
1580 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1581 NULL, NULL, TRUE, NULL);
1583 clear_callbacks(dhcp_client);
1586 * Although we register a callback here we are really not interested in
1587 * the answer because it might take too long time and network code
1588 * might be in the middle of the disconnect.
1589 * So we just inform the server that we are done with the addresses
1590 * but ignore the reply from server. This is allowed by RFC 3315
1593 g_dhcp_client_register_event(dhcp_client,
1594 G_DHCP_CLIENT_EVENT_RELEASE,
1597 dhcp->dhcp_client = dhcp_client;
1599 return g_dhcp_client_start(dhcp_client, NULL);
1602 static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
1604 DBG("dhcp %p", dhcp);
1610 if (!dhcp->dhcp_client)
1613 g_dhcp_client_stop(dhcp->dhcp_client);
1614 g_dhcp_client_unref(dhcp->dhcp_client);
1616 dhcp->dhcp_client = NULL;
1621 static void remove_network(gpointer user_data)
1623 struct connman_dhcpv6 *dhcp = user_data;
1625 DBG("dhcp %p", dhcp);
1627 dhcpv6_release(dhcp);
1632 static gboolean timeout_info_req(gpointer user_data)
1634 struct connman_dhcpv6 *dhcp = user_data;
1636 dhcp->RT = calc_delay(dhcp->RT, INF_MAX_RT);
1638 DBG("info RT timeout %d msec", dhcp->RT);
1640 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1642 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1644 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1649 static gboolean start_info_req(gpointer user_data)
1651 struct connman_dhcpv6 *dhcp = user_data;
1653 /* Set the retransmission timeout, RFC 3315 chapter 14 */
1654 dhcp->RT = initial_rt(INF_TIMEOUT);
1656 DBG("info initial RT timeout %d msec", dhcp->RT);
1658 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1660 dhcpv6_info_request(dhcp);
1665 int __connman_dhcpv6_start_info(struct connman_network *network,
1668 struct connman_dhcpv6 *dhcp;
1674 if (network_table) {
1675 dhcp = g_hash_table_lookup(network_table, network);
1676 if (dhcp && dhcp->started)
1680 dhcp = g_try_new0(struct connman_dhcpv6, 1);
1684 dhcp->network = network;
1685 dhcp->callback = callback;
1686 dhcp->stateless = true;
1687 dhcp->started = true;
1689 connman_network_ref(network);
1691 DBG("replace network %p dhcp %p", network, dhcp);
1693 g_hash_table_replace(network_table, network, dhcp);
1695 /* Initial timeout, RFC 3315, 18.1.5 */
1696 __connman_util_get_random(&rand);
1697 delay = rand % 1000;
1699 dhcp->timeout = g_timeout_add(delay, start_info_req, dhcp);
1704 static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data)
1706 struct connman_dhcpv6 *dhcp = user_data;
1708 DBG("dhcpv6 advertise msg %p", dhcp);
1712 g_dhcpv6_client_clear_retransmit(dhcp_client);
1714 if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1716 dhcp->callback(dhcp->network,
1717 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1721 dhcp->RT = initial_rt(REQ_TIMEOUT);
1722 DBG("request initial RT timeout %d msec", dhcp->RT);
1723 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1725 dhcp->request_count = 1;
1727 dhcpv6_request(dhcp, true);
1730 static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)
1732 /* We get here if server supports rapid commit */
1733 struct connman_dhcpv6 *dhcp = user_data;
1735 DBG("dhcpv6 solicitation msg %p", dhcp);
1739 g_dhcpv6_client_clear_retransmit(dhcp_client);
1741 if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1743 dhcp->callback(dhcp->network,
1744 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1748 do_dad(dhcp_client, dhcp);
1751 static gboolean timeout_solicitation(gpointer user_data)
1753 struct connman_dhcpv6 *dhcp = user_data;
1755 dhcp->RT = calc_delay(dhcp->RT, SOL_MAX_RT);
1757 DBG("solicit RT timeout %d msec", dhcp->RT);
1759 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1761 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1763 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1768 static int dhcpv6_solicitation(struct connman_dhcpv6 *dhcp)
1770 struct connman_service *service;
1771 #if !defined TIZEN_EXT
1772 struct connman_ipconfig *ipconfig_ipv6;
1774 GDHCPClient *dhcp_client;
1775 GDHCPClientError error;
1778 DBG("dhcp %p", dhcp);
1780 index = connman_network_get_index(dhcp->network);
1782 dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
1783 if (error != G_DHCP_CLIENT_ERROR_NONE) {
1788 #if !defined TIZEN_EXT
1789 if (getenv("CONNMAN_DHCPV6_DEBUG"))
1791 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
1793 service = connman_service_lookup_from_network(dhcp->network);
1796 g_dhcp_client_unref(dhcp_client);
1800 ret = set_duid(service, dhcp->network, dhcp_client, index);
1803 g_dhcp_client_unref(dhcp_client);
1807 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1808 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
1809 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1810 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1811 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1813 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1814 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1816 #if defined TIZEN_EXT
1818 When privacy extension is enabled then connman requests
1819 OPTION_IA_TA (4) from DHCPv6 server. This option is used to request
1820 temporary IPv6 address from DHCPv6 server but we found that DHCPv6
1821 server never provided temporary IPv6 address and connman resend dhcpv6
1822 requests. So always set OPTION_IA_NA in dhcpv6 request to get IPv6
1823 address from DHCPv6 server.
1825 dhcp->use_ta = FALSE;
1827 ipconfig_ipv6 = __connman_service_get_ip6config(service);
1828 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,
1833 NULL, NULL, FALSE, NULL);
1835 clear_callbacks(dhcp_client);
1837 g_dhcp_client_register_event(dhcp_client,
1838 G_DHCP_CLIENT_EVENT_SOLICITATION,
1839 solicitation_cb, dhcp);
1841 g_dhcp_client_register_event(dhcp_client,
1842 G_DHCP_CLIENT_EVENT_ADVERTISE,
1843 advertise_cb, dhcp);
1845 dhcp->dhcp_client = dhcp_client;
1847 return g_dhcp_client_start(dhcp_client, NULL);
1850 static gboolean start_solicitation(gpointer user_data)
1852 struct connman_dhcpv6 *dhcp = user_data;
1854 /* Set the retransmission timeout, RFC 3315 chapter 14 */
1855 dhcp->RT = initial_rt(SOL_TIMEOUT);
1857 DBG("solicit initial RT timeout %d msec", dhcp->RT);
1859 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1861 dhcpv6_solicitation(dhcp);
1866 int __connman_dhcpv6_start(struct connman_network *network,
1867 GSList *prefixes, dhcpv6_cb callback)
1869 struct connman_service *service;
1870 struct connman_dhcpv6 *dhcp;
1876 if (network_table) {
1877 dhcp = g_hash_table_lookup(network_table, network);
1878 if (dhcp && dhcp->started)
1882 service = connman_service_lookup_from_network(network);
1886 dhcp = g_try_new0(struct connman_dhcpv6, 1);
1890 dhcp->network = network;
1891 dhcp->callback = callback;
1892 dhcp->prefixes = prefixes;
1893 dhcp->started = true;
1895 connman_network_ref(network);
1897 DBG("replace network %p dhcp %p", network, dhcp);
1899 g_hash_table_replace(network_table, network, dhcp);
1901 /* Initial timeout, RFC 3315, 17.1.2 */
1902 __connman_util_get_random(&rand);
1903 delay = rand % 1000;
1906 * Start from scratch.
1907 * RFC 3315, chapter 17.1.2 Solicitation message
1909 * Note that we do not send CONFIRM message here as it does
1910 * not make much sense because we do not save expiration time
1911 * so we cannot really know how long the saved address is valid
1912 * anyway. The reply to CONFIRM message does not send
1913 * expiration times back to us. Because of this we need to
1914 * start using SOLICITATION anyway.
1916 dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
1921 void __connman_dhcpv6_stop(struct connman_network *network)
1928 if (g_hash_table_remove(network_table, network))
1929 connman_network_unref(network);
1932 static int save_prefixes(struct connman_ipconfig *ipconfig,
1936 int i = 0, count = g_slist_length(prefixes);
1942 array = g_try_new0(char *, count + 1);
1946 for (list = prefixes; list; list = list->next) {
1947 char *elem, addr_str[INET6_ADDRSTRLEN];
1948 GDHCPIAPrefix *prefix = list->data;
1950 elem = g_strdup_printf("%s/%d", inet_ntop(AF_INET6,
1951 &prefix->prefix, addr_str, INET6_ADDRSTRLEN),
1961 __connman_ipconfig_set_dhcpv6_prefixes(ipconfig, array);
1965 static GSList *load_prefixes(struct connman_ipconfig *ipconfig)
1968 GSList *list = NULL;
1969 char **array = __connman_ipconfig_get_dhcpv6_prefixes(ipconfig);
1974 for (i = 0; array[i]; i++) {
1975 GDHCPIAPrefix *prefix;
1977 char *ptr, **elems = g_strsplit(array[i], "/", 0);
1982 value = strtol(elems[1], &ptr, 10);
1983 if (ptr != elems[1] && *ptr == '\0' && value <= 128) {
1984 struct in6_addr addr;
1986 if (inet_pton(AF_INET6, elems[0], &addr) == 1) {
1987 prefix = g_try_new0(GDHCPIAPrefix, 1);
1992 memcpy(&prefix->prefix, &addr,
1993 sizeof(struct in6_addr));
1994 prefix->prefixlen = value;
1996 list = g_slist_prepend(list, prefix);
2006 static GDHCPIAPrefix *copy_prefix(gpointer data)
2008 GDHCPIAPrefix *copy, *prefix = data;
2010 copy = g_try_new(GDHCPIAPrefix, 1);
2014 memcpy(copy, prefix, sizeof(GDHCPIAPrefix));
2019 static GSList *copy_and_convert_prefixes(GList *prefixes)
2021 GSList *copy = NULL;
2024 for (list = prefixes; list; list = list->next)
2025 copy = g_slist_prepend(copy, copy_prefix(list->data));
2030 static int set_prefixes(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
2033 g_slist_free_full(dhcp->prefixes, free_prefix);
2036 copy_and_convert_prefixes(g_dhcp_client_get_option(dhcp_client,
2039 DBG("Got %d prefix", g_slist_length(dhcp->prefixes));
2041 if (dhcp->callback) {
2042 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
2043 if (status == G_DHCPV6_ERROR_NO_PREFIX)
2044 dhcp->callback(dhcp->network,
2045 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2047 struct connman_service *service;
2048 struct connman_ipconfig *ipconfig;
2049 int ifindex = connman_network_get_index(dhcp->network);
2051 service = __connman_service_lookup_from_index(ifindex);
2053 ipconfig = __connman_service_get_ip6config(
2055 save_prefixes(ipconfig, dhcp->prefixes);
2056 __connman_service_save(service);
2059 dhcp->callback(dhcp->network,
2060 CONNMAN_DHCPV6_STATUS_SUCCEED, dhcp->prefixes);
2063 g_slist_free_full(dhcp->prefixes, free_prefix);
2064 dhcp->prefixes = NULL;
2070 static void re_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2072 struct connman_dhcpv6 *dhcp = user_data;
2076 status = g_dhcpv6_client_get_status(dhcp_client);
2078 DBG("dhcpv6 cb msg %p status %d", dhcp, status);
2080 if (status == G_DHCPV6_ERROR_BINDING) {
2081 /* RFC 3315, 18.1.8 */
2082 dhcpv6_pd_request(dhcp);
2084 ret = set_prefixes(dhcp_client, dhcp);
2087 dhcp->callback(dhcp->network,
2088 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2094 static void rebind_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2098 g_dhcpv6_client_clear_retransmit(dhcp_client);
2100 re_pd_cb(dhcp_client, user_data);
2103 static GDHCPClient *create_pd_client(struct connman_dhcpv6 *dhcp, int *err)
2105 GDHCPClient *dhcp_client;
2106 GDHCPClientError error;
2107 struct connman_service *service;
2111 index = connman_network_get_index(dhcp->network);
2113 dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
2114 if (error != G_DHCP_CLIENT_ERROR_NONE) {
2119 #if !defined TIZEN_EXT
2120 if (getenv("CONNMAN_DHCPV6_DEBUG"))
2122 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6:PD");
2124 service = connman_service_lookup_from_network(dhcp->network);
2126 g_dhcp_client_unref(dhcp_client);
2131 ret = set_duid(service, dhcp->network, dhcp_client, index);
2133 g_dhcp_client_unref(dhcp_client);
2138 g_dhcpv6_client_create_iaid(dhcp_client, index, (unsigned char *)&iaid);
2139 g_dhcpv6_client_set_iaid(dhcp_client, iaid);
2144 static int dhcpv6_pd_rebind(struct connman_dhcpv6 *dhcp)
2146 GDHCPClient *dhcp_client;
2149 DBG("dhcp %p", dhcp);
2151 if (!dhcp->dhcp_client) {
2153 * We skipped the solicitation phase
2157 dhcp->dhcp_client = create_pd_client(dhcp, &err);
2158 if (!dhcp->dhcp_client) {
2164 dhcp_client = dhcp->dhcp_client;
2166 g_dhcp_client_clear_requests(dhcp_client);
2168 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2169 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2170 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2172 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2173 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2175 clear_callbacks(dhcp_client);
2177 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
2178 rebind_pd_cb, dhcp);
2180 return g_dhcp_client_start(dhcp_client, NULL);
2183 static void renew_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2187 g_dhcpv6_client_clear_retransmit(dhcp_client);
2189 re_pd_cb(dhcp_client, user_data);
2192 static int dhcpv6_pd_renew(struct connman_dhcpv6 *dhcp)
2194 GDHCPClient *dhcp_client;
2197 DBG("dhcp %p", dhcp);
2199 dhcp_client = dhcp->dhcp_client;
2201 g_dhcp_client_clear_requests(dhcp_client);
2203 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2204 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2205 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2206 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2208 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2209 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2211 clear_callbacks(dhcp_client);
2213 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
2216 return g_dhcp_client_start(dhcp_client, NULL);
2220 * Check if we need to restart the solicitation procedure. This
2221 * is done if all the prefixes have expired.
2223 static int check_pd_restart(struct connman_dhcpv6 *dhcp)
2225 time_t current, expired;
2227 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
2229 current = time(NULL);
2231 if (current > expired) {
2232 DBG("expired by %d secs", (int)(current - expired));
2234 g_idle_add(dhcpv6_restart, dhcp);
2242 static gboolean timeout_pd_rebind(gpointer user_data)
2244 struct connman_dhcpv6 *dhcp = user_data;
2246 if (check_pd_restart(dhcp) < 0)
2249 dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
2251 DBG("rebind RT timeout %d msec", dhcp->RT);
2253 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2255 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2257 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2262 static gboolean start_pd_rebind(gpointer user_data)
2264 struct connman_dhcpv6 *dhcp = user_data;
2266 if (check_pd_restart(dhcp) < 0)
2269 dhcp->RT = initial_rt(REB_TIMEOUT);
2271 DBG("rebind initial RT timeout %d msec", dhcp->RT);
2273 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2275 dhcpv6_pd_rebind(dhcp);
2280 static gboolean timeout_pd_rebind_confirm(gpointer user_data)
2282 struct connman_dhcpv6 *dhcp = user_data;
2284 dhcp->RT = calc_delay(dhcp->RT, CNF_MAX_RT);
2286 DBG("rebind with confirm RT timeout %d msec", dhcp->RT);
2288 dhcp->timeout = g_timeout_add(dhcp->RT,
2289 timeout_pd_rebind_confirm, dhcp);
2291 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2293 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2298 static gboolean timeout_pd_max_confirm(gpointer user_data)
2300 struct connman_dhcpv6 *dhcp = user_data;
2306 DBG("rebind with confirm max retransmit duration timeout");
2308 g_dhcpv6_client_clear_retransmit(dhcp->dhcp_client);
2311 dhcp->callback(dhcp->network,
2312 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2317 static gboolean start_pd_rebind_with_confirm(gpointer user_data)
2319 struct connman_dhcpv6 *dhcp = user_data;
2321 dhcp->RT = initial_rt(CNF_TIMEOUT);
2323 DBG("rebind with confirm initial RT timeout %d msec", dhcp->RT);
2325 dhcp->timeout = g_timeout_add(dhcp->RT,
2326 timeout_pd_rebind_confirm, dhcp);
2327 dhcp->MRD = g_timeout_add(CNF_MAX_RD, timeout_pd_max_confirm, dhcp);
2329 dhcpv6_pd_rebind(dhcp);
2334 static gboolean timeout_pd_renew(gpointer user_data)
2336 struct connman_dhcpv6 *dhcp = user_data;
2338 if (check_pd_restart(dhcp) < 0)
2341 dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
2343 DBG("renew RT timeout %d msec", dhcp->RT);
2345 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
2347 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2349 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2354 static gboolean start_pd_renew(gpointer user_data)
2356 struct connman_dhcpv6 *dhcp = user_data;
2358 dhcp->RT = initial_rt(REN_TIMEOUT);
2360 DBG("renew initial RT timeout %d msec", dhcp->RT);
2362 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_renew, dhcp);
2364 dhcpv6_pd_renew(dhcp);
2369 int __connman_dhcpv6_start_pd_renew(struct connman_network *network,
2372 struct connman_dhcpv6 *dhcp;
2374 time_t started, current, expired;
2376 dhcp = g_hash_table_lookup(network_pd_table, network);
2380 DBG("network %p dhcp %p", network, dhcp);
2384 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
2385 &started, &expired);
2387 current = time(NULL);
2389 DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
2390 expired, current, started);
2392 if (T1 == 0xffffffff)
2393 /* RFC 3633, ch 9 */
2398 * Client can choose the timeout.
2402 dhcp->callback = callback;
2404 /* RFC 3315, 18.1.4, start solicit if expired */
2405 if (check_pd_restart(dhcp) < 0)
2408 if (T2 != 0xffffffff && T2 > 0) {
2409 if ((unsigned)current >= (unsigned)started + T2) {
2410 /* RFC 3315, chapter 18.1.3, start rebind */
2411 DBG("rebind after %d secs", T2);
2413 dhcp->timeout = g_timeout_add_seconds(T2,
2417 } else if ((unsigned)current < (unsigned)started + T1) {
2418 DBG("renew after %d secs", T1);
2420 dhcp->timeout = g_timeout_add_seconds(T1,
2424 DBG("rebind after %d secs", T2 - T1);
2426 dhcp->timeout = g_timeout_add_seconds(T2 - T1,
2435 static void release_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2440 int __connman_dhcpv6_start_pd_release(struct connman_network *network,
2443 struct connman_dhcpv6 *dhcp;
2444 GDHCPClient *dhcp_client;
2448 return 0; /* we are already released */
2450 dhcp = g_hash_table_lookup(network_pd_table, network);
2454 DBG("network %p dhcp %p client %p", network, dhcp, dhcp->dhcp_client);
2458 dhcp_client = dhcp->dhcp_client;
2460 DBG("DHCPv6 PD was not started");
2464 g_dhcp_client_clear_requests(dhcp_client);
2465 g_dhcp_client_clear_values(dhcp_client);
2467 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2468 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2470 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2471 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2473 clear_callbacks(dhcp_client);
2475 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RELEASE,
2476 release_pd_cb, dhcp);
2478 dhcp->dhcp_client = dhcp_client;
2480 return g_dhcp_client_start(dhcp_client, NULL);
2483 static gboolean timeout_pd_request(gpointer user_data)
2485 struct connman_dhcpv6 *dhcp = user_data;
2487 if (dhcp->request_count >= REQ_MAX_RC) {
2488 DBG("max request retry attempts %d", dhcp->request_count);
2489 dhcp->request_count = 0;
2491 dhcp->callback(dhcp->network,
2492 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2496 dhcp->request_count++;
2498 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
2499 DBG("request RT timeout %d msec", dhcp->RT);
2500 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2502 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2504 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2509 static void request_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2511 struct connman_dhcpv6 *dhcp = user_data;
2516 g_dhcpv6_client_clear_retransmit(dhcp_client);
2518 status = g_dhcpv6_client_get_status(dhcp_client);
2520 DBG("dhcpv6 pd cb msg %p status %d", dhcp, status);
2522 if (status == G_DHCPV6_ERROR_BINDING) {
2523 /* RFC 3315, 18.1.8 */
2524 dhcpv6_pd_request(dhcp);
2526 set_prefixes(dhcp_client, dhcp);
2530 static int dhcpv6_pd_request(struct connman_dhcpv6 *dhcp)
2532 GDHCPClient *dhcp_client;
2533 uint32_t T1 = 0, T2 = 0;
2535 DBG("dhcp %p", dhcp);
2537 dhcp_client = dhcp->dhcp_client;
2539 g_dhcp_client_clear_requests(dhcp_client);
2541 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2542 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2543 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2544 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2546 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2547 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2549 clear_callbacks(dhcp_client);
2551 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
2552 request_pd_cb, dhcp);
2554 return g_dhcp_client_start(dhcp_client, NULL);
2557 static void advertise_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2559 struct connman_dhcpv6 *dhcp = user_data;
2561 DBG("dhcpv6 advertise pd msg %p", dhcp);
2565 g_dhcpv6_client_clear_retransmit(dhcp_client);
2567 if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
2569 dhcp->callback(dhcp->network,
2570 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2574 dhcp->RT = initial_rt(REQ_TIMEOUT);
2575 DBG("request initial RT timeout %d msec", dhcp->RT);
2576 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2578 dhcp->request_count = 1;
2580 dhcpv6_pd_request(dhcp);
2583 static void solicitation_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2586 * This callback is here so that g_dhcp_client_start()
2587 * will enter the proper L3 mode.
2589 DBG("DHCPv6 %p solicitation msg received, ignoring it", user_data);
2592 static int dhcpv6_pd_solicitation(struct connman_dhcpv6 *dhcp)
2594 GDHCPClient *dhcp_client;
2597 DBG("dhcp %p", dhcp);
2599 dhcp_client = create_pd_client(dhcp, &ret);
2605 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2607 g_dhcpv6_client_set_pd(dhcp_client, NULL, NULL, NULL);
2609 clear_callbacks(dhcp_client);
2611 g_dhcp_client_register_event(dhcp_client,
2612 G_DHCP_CLIENT_EVENT_ADVERTISE,
2613 advertise_pd_cb, dhcp);
2615 g_dhcp_client_register_event(dhcp_client,
2616 G_DHCP_CLIENT_EVENT_SOLICITATION,
2617 solicitation_pd_cb, dhcp);
2619 dhcp->dhcp_client = dhcp_client;
2621 return g_dhcp_client_start(dhcp_client, NULL);
2624 static gboolean start_pd_solicitation(gpointer user_data)
2626 struct connman_dhcpv6 *dhcp = user_data;
2628 /* Set the retransmission timeout, RFC 3315 chapter 14 */
2629 dhcp->RT = initial_rt(SOL_TIMEOUT);
2631 DBG("solicit initial RT timeout %d msec", dhcp->RT);
2633 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
2635 dhcpv6_pd_solicitation(dhcp);
2640 int __connman_dhcpv6_start_pd(int index, GSList *prefixes, dhcpv6_cb callback)
2642 struct connman_service *service;
2643 struct connman_network *network;
2644 struct connman_dhcpv6 *dhcp;
2649 DBG("index %d", index);
2651 service = __connman_service_lookup_from_index(index);
2655 network = __connman_service_get_network(service);
2659 if (network_pd_table) {
2660 dhcp = g_hash_table_lookup(network_pd_table, network);
2661 if (dhcp && dhcp->started)
2665 dhcp = g_try_new0(struct connman_dhcpv6, 1);
2669 dhcp->network = network;
2670 dhcp->callback = callback;
2671 dhcp->started = true;
2675 * Try to load the earlier prefixes if caller did not supply
2676 * any that we could use.
2678 struct connman_ipconfig *ipconfig;
2679 ipconfig = __connman_service_get_ip6config(service);
2681 dhcp->prefixes = load_prefixes(ipconfig);
2683 dhcp->prefixes = prefixes;
2685 connman_network_ref(network);
2687 DBG("replace network %p dhcp %p", network, dhcp);
2689 g_hash_table_replace(network_pd_table, network, dhcp);
2691 if (!dhcp->prefixes) {
2693 * Refresh start, try to get prefixes.
2695 start_pd_solicitation(dhcp);
2698 * We used to have prefixes, try to use them again.
2699 * We need to use timeouts from confirm msg, RFC 3633, ch 12.1
2701 start_pd_rebind_with_confirm(dhcp);
2707 void __connman_dhcpv6_stop_pd(int index)
2709 struct connman_service *service;
2710 struct connman_network *network;
2715 DBG("index %d", index);
2717 if (!network_pd_table)
2720 service = __connman_service_lookup_from_index(index);
2724 network = __connman_service_get_network(service);
2728 __connman_dhcpv6_start_pd_release(network, NULL);
2730 if (g_hash_table_remove(network_pd_table, network))
2731 connman_network_unref(network);
2734 int __connman_dhcpv6_init(void)
2738 network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2739 NULL, remove_network);
2741 network_pd_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2742 NULL, remove_network);
2747 void __connman_dhcpv6_cleanup(void)
2751 g_hash_table_destroy(network_table);
2752 network_table = NULL;
2754 g_hash_table_destroy(network_pd_table);
2755 network_pd_table = NULL;