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 guint compute_random(guint val)
112 __connman_util_get_random(&rand);
114 return val - val / 10 +
115 ((guint) rand % (2 * 1000)) * val / 10 / 1000;
118 /* Calculate a random delay, RFC 3315 chapter 14 */
119 /* RT and MRT are milliseconds */
120 static guint calc_delay(guint RT, guint MRT)
122 if (MRT && (RT > MRT / 2))
123 RT = compute_random(MRT);
125 RT += compute_random(RT);
130 static guint initial_rt(guint timeout)
132 return compute_random(timeout);
135 static void free_prefix(gpointer data)
140 static void dhcpv6_free(struct connman_dhcpv6 *dhcp)
142 g_strfreev(dhcp->nameservers);
143 g_strfreev(dhcp->timeservers);
145 dhcp->nameservers = NULL;
146 dhcp->timeservers = NULL;
147 dhcp->started = false;
149 g_slist_free_full(dhcp->prefixes, free_prefix);
152 static bool compare_string_arrays(char **array_a, char **array_b)
156 if (!array_a || !array_b)
159 if (g_strv_length(array_a) != g_strv_length(array_b))
162 for (i = 0; array_a[i] && array_b[i]; i++)
163 if (g_strcmp0(array_a[i], array_b[i]) != 0)
169 static void dhcpv6_debug(const char *str, void *data)
171 connman_info("%s: %s\n", (const char *) data, str);
174 static gchar *convert_to_hex(unsigned char *buf, int len)
176 gchar *ret = g_try_malloc(len * 2 + 1);
179 for (i = 0; ret && i < len; i++)
180 g_snprintf(ret + i * 2, 3, "%02x", buf[i]);
186 * DUID should not change over time so save it to file.
187 * See RFC 3315 chapter 9 for details.
189 static int set_duid(struct connman_service *service,
190 struct connman_network *network,
191 GDHCPClient *dhcp_client, int index)
199 ident = connman_service_get_identifier(service);
200 #if defined TIZEN_EXT
202 DBG("ident : %s", ident);
205 keyfile = connman_storage_load_service(ident);
207 #if defined TIZEN_EXT
209 keyfile = g_key_file_new();
218 hex_duid = g_key_file_get_string(keyfile, ident, "IPv6.DHCP.DUID",
221 unsigned int i, j = 0, hex;
222 size_t hex_duid_len = strlen(hex_duid);
224 duid = g_try_malloc0(hex_duid_len / 2);
226 g_key_file_free(keyfile);
231 for (i = 0; i < hex_duid_len; i += 2) {
232 sscanf(hex_duid + i, "%02x", &hex);
236 duid_len = hex_duid_len / 2;
239 int type = __connman_ipconfig_get_type_from_index(index);
241 ret = g_dhcpv6_create_duid(G_DHCPV6_DUID_LLT, index, type,
244 g_key_file_free(keyfile);
248 hex_duid = convert_to_hex(duid, duid_len);
251 g_key_file_free(keyfile);
255 g_key_file_set_string(keyfile, ident, "IPv6.DHCP.DUID",
258 __connman_storage_save_service(keyfile, ident);
262 g_key_file_free(keyfile);
264 g_dhcpv6_client_set_duid(dhcp_client, duid, duid_len);
269 static void clear_callbacks(GDHCPClient *dhcp_client)
271 g_dhcp_client_register_event(dhcp_client,
272 G_DHCP_CLIENT_EVENT_SOLICITATION,
275 g_dhcp_client_register_event(dhcp_client,
276 G_DHCP_CLIENT_EVENT_ADVERTISE,
279 g_dhcp_client_register_event(dhcp_client,
280 G_DHCP_CLIENT_EVENT_REQUEST,
283 g_dhcp_client_register_event(dhcp_client,
284 G_DHCP_CLIENT_EVENT_CONFIRM,
287 g_dhcp_client_register_event(dhcp_client,
288 G_DHCP_CLIENT_EVENT_RENEW,
291 g_dhcp_client_register_event(dhcp_client,
292 G_DHCP_CLIENT_EVENT_REBIND,
295 g_dhcp_client_register_event(dhcp_client,
296 G_DHCP_CLIENT_EVENT_RELEASE,
299 g_dhcp_client_register_event(dhcp_client,
300 G_DHCP_CLIENT_EVENT_DECLINE,
303 g_dhcp_client_register_event(dhcp_client,
304 G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
308 static void info_req_cb(GDHCPClient *dhcp_client, gpointer user_data)
310 struct connman_dhcpv6 *dhcp = user_data;
311 struct connman_service *service;
313 GList *option, *list;
314 char **nameservers, **timeservers;
316 DBG("dhcpv6 information-request %p", dhcp);
318 service = connman_service_lookup_from_network(dhcp->network);
320 connman_error("Can not lookup service");
324 g_dhcpv6_client_clear_retransmit(dhcp_client);
326 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
327 entries = g_list_length(option);
329 nameservers = g_try_new0(char *, entries + 1);
331 for (i = 0, list = option; list; list = list->next, i++)
332 nameservers[i] = g_strdup(list->data);
335 if (!compare_string_arrays(nameservers, dhcp->nameservers)) {
336 if (dhcp->nameservers) {
337 for (i = 0; dhcp->nameservers[i]; i++)
338 #if defined TIZEN_EXT
340 __connman_service_nameserver_remove(service,
341 dhcp->nameservers[i], false,
342 CONNMAN_IPCONFIG_TYPE_IPV6);
344 __connman_service_nameserver_remove(service,
345 dhcp->nameservers[i],
348 #if defined TIZEN_EXT
351 g_strfreev(dhcp->nameservers);
354 dhcp->nameservers = nameservers;
356 for (i = 0; dhcp->nameservers &&
357 dhcp->nameservers[i]; i++)
358 #if defined TIZEN_EXT
360 __connman_service_nameserver_append(service,
361 dhcp->nameservers[i], false,
362 CONNMAN_IPCONFIG_TYPE_IPV6);
364 __connman_service_nameserver_append(service,
365 dhcp->nameservers[i],
368 #if defined TIZEN_EXT
372 g_strfreev(nameservers);
375 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
376 entries = g_list_length(option);
378 timeservers = g_try_new0(char *, entries + 1);
380 for (i = 0, list = option; list; list = list->next, i++)
381 timeservers[i] = g_strdup(list->data);
384 if (!compare_string_arrays(timeservers, dhcp->timeservers)) {
385 if (dhcp->timeservers) {
386 for (i = 0; dhcp->timeservers[i]; i++)
387 __connman_service_timeserver_remove(service,
388 dhcp->timeservers[i]);
389 g_strfreev(dhcp->timeservers);
392 dhcp->timeservers = timeservers;
394 for (i = 0; dhcp->timeservers &&
395 dhcp->timeservers[i]; i++)
396 __connman_service_timeserver_append(service,
397 dhcp->timeservers[i]);
399 g_strfreev(timeservers);
402 if (dhcp->callback) {
403 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
404 dhcp->callback(dhcp->network, status == 0 ?
405 CONNMAN_DHCPV6_STATUS_SUCCEED :
406 CONNMAN_DHCPV6_STATUS_FAIL,
411 static int dhcpv6_info_request(struct connman_dhcpv6 *dhcp)
413 struct connman_service *service;
414 GDHCPClient *dhcp_client;
415 GDHCPClientError error;
418 DBG("dhcp %p", dhcp);
420 index = connman_network_get_index(dhcp->network);
422 dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
423 if (error != G_DHCP_CLIENT_ERROR_NONE) {
428 #if !defined TIZEN_EXT
429 if (getenv("CONNMAN_DHCPV6_DEBUG"))
431 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
433 service = connman_service_lookup_from_network(dhcp->network);
436 g_dhcp_client_unref(dhcp_client);
440 ret = set_duid(service, dhcp->network, dhcp_client, index);
443 g_dhcp_client_unref(dhcp_client);
447 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
448 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
449 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
450 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
452 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
453 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
455 g_dhcp_client_register_event(dhcp_client,
456 G_DHCP_CLIENT_EVENT_INFORMATION_REQ, info_req_cb, dhcp);
458 dhcp->dhcp_client = dhcp_client;
460 return g_dhcp_client_start(dhcp_client, NULL);
463 static int check_ipv6_addr_prefix(GSList *prefixes, char *address)
465 struct in6_addr addr_prefix, addr;
469 for (list = prefixes; list; list = list->next) {
470 char *prefix = list->data;
471 const char *slash = g_strrstr(prefix, "/");
472 const unsigned char bits[] = { 0x00, 0xFE, 0xFC, 0xF8,
473 0xF0, 0xE0, 0xC0, 0x80 };
474 int left, count, i, plen;
479 len = strtol(slash + 1, NULL, 10);
480 if (len < 3 || len > 128)
489 prefix = g_strndup(prefix, slash - prefix);
490 inet_pton(AF_INET6, prefix, &addr_prefix);
491 inet_pton(AF_INET6, address, &addr);
493 memset(&addr_prefix.s6_addr[i], 0, count);
494 memset(&addr.s6_addr[i], 0, count);
497 addr_prefix.s6_addr[i - 1] &= bits[left];
498 addr.s6_addr[i - 1] &= bits[left];
503 if (memcmp(&addr_prefix, &addr, 16) == 0) {
512 static int set_other_addresses(GDHCPClient *dhcp_client,
513 struct connman_dhcpv6 *dhcp)
515 struct connman_service *service;
517 GList *option, *list;
518 char **nameservers, **timeservers;
520 service = connman_service_lookup_from_network(dhcp->network);
522 connman_error("Can not lookup service");
527 * Check domains before nameservers so that the nameserver append
528 * function will update domain list in service.c
530 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DOMAIN_LIST);
531 entries = g_list_length(option);
533 char **domains = g_try_new0(char *, entries + 1);
535 for (i = 0, list = option; list;
536 list = list->next, i++)
537 domains[i] = g_strdup(list->data);
538 __connman_service_set_search_domains(service, domains);
543 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
544 entries = g_list_length(option);
546 nameservers = g_try_new0(char *, entries + 1);
548 for (i = 0, list = option; list; list = list->next, i++)
549 nameservers[i] = g_strdup(list->data);
552 if (!compare_string_arrays(nameservers, dhcp->nameservers)) {
553 if (dhcp->nameservers) {
554 for (i = 0; dhcp->nameservers[i]; i++)
555 #if defined TIZEN_EXT
557 __connman_service_nameserver_remove(service,
558 dhcp->nameservers[i],
559 false, CONNMAN_IPCONFIG_TYPE_IPV6);
561 __connman_service_nameserver_remove(service,
562 dhcp->nameservers[i],
565 #if defined TIZEN_EXT
568 g_strfreev(dhcp->nameservers);
571 dhcp->nameservers = nameservers;
573 for (i = 0; dhcp->nameservers &&
574 dhcp->nameservers[i]; i++)
575 #if defined TIZEN_EXT
577 __connman_service_nameserver_append(service,
578 dhcp->nameservers[i],
579 false, CONNMAN_IPCONFIG_TYPE_IPV6);
581 __connman_service_nameserver_append(service,
582 dhcp->nameservers[i],
585 #if defined TIZEN_EXT
589 g_strfreev(nameservers);
592 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
593 entries = g_list_length(option);
595 timeservers = g_try_new0(char *, entries + 1);
597 for (i = 0, list = option; list; list = list->next, i++)
598 timeservers[i] = g_strdup(list->data);
601 if (!compare_string_arrays(timeservers, dhcp->timeservers)) {
602 if (dhcp->timeservers) {
603 for (i = 0; dhcp->timeservers[i]; i++)
604 __connman_service_timeserver_remove(service,
605 dhcp->timeservers[i]);
606 g_strfreev(dhcp->timeservers);
609 dhcp->timeservers = timeservers;
611 for (i = 0; dhcp->timeservers &&
612 dhcp->timeservers[i]; i++)
613 __connman_service_timeserver_append(service,
614 dhcp->timeservers[i]);
616 g_strfreev(timeservers);
621 static GSList *copy_prefixes(GSList *prefixes)
623 GSList *list, *copy = NULL;
625 for (list = prefixes; list; list = list->next)
626 copy = g_slist_prepend(copy, g_strdup(list->data));
632 * Helper struct for doing DAD (duplicate address detection).
633 * It is refcounted and freed after all reply's to neighbor
634 * discovery request are received.
640 GDHCPClient *dhcp_client;
641 struct connman_ipconfig *ipconfig;
649 static void free_own_address(struct own_address *data)
651 g_dhcp_client_unref(data->dhcp_client);
652 __connman_ipconfig_unref(data->ipconfig);
653 g_slist_free_full(data->prefixes, free_prefix);
654 g_slist_free_full(data->dad_failed, g_free);
655 g_slist_free_full(data->dad_succeed, g_free);
660 static struct own_address *ref_own_address(struct own_address *address)
662 DBG("%p ref %d", address, address->refcount + 1);
664 __sync_fetch_and_add(&address->refcount, 1);
669 static void unref_own_address(struct own_address *address)
674 DBG("%p ref %d", address, address->refcount - 1);
676 if (__sync_fetch_and_sub(&address->refcount, 1) != 1)
679 free_own_address(address);
682 static void set_address(int ifindex, struct connman_ipconfig *ipconfig,
683 GSList *prefixes, char *address)
685 const char *c_address;
687 c_address = __connman_ipconfig_get_local(ipconfig);
689 if (address && ((c_address && g_strcmp0(address, c_address) != 0) ||
693 /* Is this prefix part of the subnet we are suppose to use? */
694 prefix_len = check_ipv6_addr_prefix(prefixes, address);
696 #if defined TIZEN_EXT
697 char *gateway = g_strdup(__connman_ipconfig_get_gateway(ipconfig));
699 __connman_ipconfig_address_remove(ipconfig);
700 __connman_ipconfig_set_local(ipconfig, address);
701 __connman_ipconfig_set_prefixlen(ipconfig, prefix_len);
703 DBG("new address %s/%d", address, prefix_len);
705 __connman_ipconfig_set_dhcp_address(ipconfig, address);
706 #if defined TIZEN_EXT
707 DBG("Set gateway %s", gateway);
708 __connman_ipconfig_set_gateway(ipconfig, gateway);
711 __connman_service_save(
712 __connman_service_lookup_from_index(ifindex));
718 * Helper struct that is used when waiting a reply to DECLINE message.
720 struct decline_cb_data {
721 GDHCPClient *dhcp_client;
727 static void decline_reply_callback(struct decline_cb_data *data)
729 struct connman_network *network;
730 struct connman_service *service;
732 service = __connman_service_lookup_from_index(data->ifindex);
733 network = __connman_service_get_network(service);
736 data->callback(network, CONNMAN_DHCPV6_STATUS_RESTART, NULL);
738 g_dhcp_client_unref(data->dhcp_client);
741 static gboolean decline_timeout(gpointer user_data)
743 struct decline_cb_data *data = user_data;
745 DBG("ifindex %d", data->ifindex);
748 * We ignore all DECLINE replies that are received after the timeout
750 g_dhcp_client_register_event(data->dhcp_client,
751 G_DHCP_CLIENT_EVENT_DECLINE,
754 decline_reply_callback(data);
761 static void decline_cb(GDHCPClient *dhcp_client, gpointer user_data)
763 struct decline_cb_data *data = user_data;
765 DBG("ifindex %d", data->ifindex);
767 g_dhcpv6_client_clear_retransmit(dhcp_client);
770 g_source_remove(data->timeout);
772 decline_reply_callback(data);
777 static int dhcpv6_decline(GDHCPClient *dhcp_client, int ifindex,
778 dhcpv6_cb callback, GSList *failed)
780 struct decline_cb_data *data;
784 DBG("dhcp_client %p", dhcp_client);
786 g_dhcp_client_clear_requests(dhcp_client);
788 g_dhcpv6_client_clear_send(dhcp_client, G_DHCPV6_ORO);
790 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
791 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
793 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
795 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
797 code = G_DHCPV6_IA_TA;
801 code = G_DHCPV6_IA_NA;
803 g_dhcpv6_client_clear_send(dhcp_client, code);
805 g_dhcpv6_client_set_ias(dhcp_client, ifindex, code, NULL, NULL,
808 clear_callbacks(dhcp_client);
810 data = g_try_new(struct decline_cb_data, 1);
814 data->ifindex = ifindex;
815 data->callback = callback;
816 data->dhcp_client = g_dhcp_client_ref(dhcp_client);
817 data->timeout = g_timeout_add(DEC_TIMEOUT, decline_timeout, data);
819 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_DECLINE,
822 return g_dhcp_client_start(dhcp_client, NULL);
825 static void dad_reply(struct nd_neighbor_advert *reply,
826 unsigned int length, struct in6_addr *addr, void *user_data)
828 struct own_address *data = user_data;
830 char address[INET6_ADDRSTRLEN];
831 enum __connman_dhcpv6_status status = CONNMAN_DHCPV6_STATUS_FAIL;
833 inet_ntop(AF_INET6, addr, address, INET6_ADDRSTRLEN);
835 DBG("user %p reply %p len %d address %s index %d data %p", user_data,
836 reply, length, address, data->ifindex, data);
840 DBG("DAD succeed for %s", address);
842 DBG("DAD cannot be done for %s", address);
844 data->dad_succeed = g_slist_prepend(data->dad_succeed,
848 DBG("DAD failed for %s", address);
850 data->dad_failed = g_slist_prepend(data->dad_failed,
855 * If refcount == 1 then we got the final reply and can continue.
857 if (data->refcount > 1)
860 for (list = data->dad_succeed; list; list = list->next)
861 set_address(data->ifindex, data->ipconfig, data->prefixes,
864 if (data->dad_failed) {
865 dhcpv6_decline(data->dhcp_client, data->ifindex,
866 data->callback, data->dad_failed);
868 if (data->dad_succeed)
869 status = CONNMAN_DHCPV6_STATUS_SUCCEED;
871 if (data->callback) {
872 struct connman_network *network;
873 struct connman_service *service;
875 service = __connman_service_lookup_from_index(
877 network = __connman_service_get_network(service);
879 data->callback(network, status, NULL);
883 unref_own_address(data);
887 * Is the kernel configured to do DAD? If 0, then do not do DAD.
888 * See also RFC 4862 chapter 5.4 about DupAddrDetectTransmits
890 static int dad_transmits(int ifindex)
892 char name[IF_NAMESIZE];
897 if (!if_indextoname(ifindex, name))
900 path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/dad_transmits",
906 f = fopen(path, "r");
911 if (fscanf(f, "%d", &value) < 0)
920 static void do_dad(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
922 struct connman_service *service;
923 struct connman_ipconfig *ipconfig;
925 GList *option, *list;
926 struct own_address *user_data;
928 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
930 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
933 * Even if we didn't had any addresses, just try to set
936 set_other_addresses(dhcp_client, dhcp);
939 DBG("Skip DAD as no addresses found in reply");
941 dhcp->callback(dhcp->network,
942 CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
947 ifindex = connman_network_get_index(dhcp->network);
949 DBG("index %d", ifindex);
951 service = connman_service_lookup_from_network(dhcp->network);
953 connman_error("Can not lookup service for index %d", ifindex);
957 ipconfig = __connman_service_get_ip6config(service);
959 connman_error("Could not lookup ip6config for index %d",
964 if (!dad_transmits(ifindex)) {
965 DBG("Skip DAD because of kernel configuration");
967 for (list = option; list; list = list->next)
968 set_address(ifindex, ipconfig, dhcp->prefixes,
972 dhcp->callback(dhcp->network,
973 CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
978 if (g_list_length(option) == 0) {
979 DBG("No addresses when doing DAD");
981 dhcp->callback(dhcp->network,
982 CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
987 user_data = g_try_new0(struct own_address, 1);
991 user_data->refcount = 0;
992 user_data->ifindex = ifindex;
993 user_data->dhcp_client = g_dhcp_client_ref(dhcp_client);
994 user_data->ipconfig = __connman_ipconfig_ref(ipconfig);
995 user_data->prefixes = copy_prefixes(dhcp->prefixes);
996 user_data->callback = dhcp->callback;
999 * We send one neighbor discovery request / address
1000 * and after all checks are done, then report the status
1001 * via dhcp callback.
1004 for (list = option; list; list = list->next) {
1005 char *address = option->data;
1006 struct in6_addr addr;
1009 ref_own_address(user_data);
1011 if (inet_pton(AF_INET6, address, &addr) < 0) {
1012 DBG("Invalid IPv6 address %s %d/%s", address,
1013 -errno, strerror(errno));
1017 DBG("user %p address %s client %p ipconfig %p prefixes %p",
1019 user_data->dhcp_client, user_data->ipconfig,
1020 user_data->prefixes);
1022 ret = __connman_inet_ipv6_do_dad(ifindex, 1000,
1027 DBG("Could not send neighbor solicitation for %s",
1029 dad_reply(NULL, -1, &addr, user_data);
1031 DBG("Sent neighbor solicitation %d bytes", ret);
1038 unref_own_address(user_data);
1042 dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
1046 static gboolean timeout_request_resend(gpointer user_data)
1048 struct connman_dhcpv6 *dhcp = user_data;
1050 if (dhcp->request_count >= REQ_MAX_RC) {
1051 DBG("max request retry attempts %d", dhcp->request_count);
1052 dhcp->request_count = 0;
1054 dhcp->callback(dhcp->network,
1055 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1059 dhcp->request_count++;
1061 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1062 DBG("request resend RT timeout %d msec", dhcp->RT);
1063 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request_resend, dhcp);
1065 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1067 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1072 static gboolean request_resend(gpointer user_data)
1074 struct connman_dhcpv6 *dhcp = user_data;
1076 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1078 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1079 DBG("request resend RT timeout %d msec", dhcp->RT);
1080 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request_resend, dhcp);
1082 dhcpv6_request(dhcp, true);
1087 static void do_resend_request(struct connman_dhcpv6 *dhcp)
1089 if (dhcp->request_count >= REQ_MAX_RC) {
1090 DBG("max request retry attempts %d", dhcp->request_count);
1091 dhcp->request_count = 0;
1093 dhcp->callback(dhcp->network,
1094 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1098 dhcp->request_count++;
1100 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1101 DBG("resending request after %d msec", dhcp->RT);
1102 dhcp->timeout = g_timeout_add(dhcp->RT, request_resend, dhcp);
1105 static void re_cb(enum request_type req_type, GDHCPClient *dhcp_client,
1108 struct connman_dhcpv6 *dhcp = user_data;
1113 status = g_dhcpv6_client_get_status(dhcp_client);
1115 DBG("dhcpv6 cb msg %p status %d", dhcp, status);
1118 * RFC 3315, 18.1.8 handle the resend if error
1120 if (status == G_DHCPV6_ERROR_BINDING) {
1121 dhcpv6_request(dhcp, false);
1122 } else if (status == G_DHCPV6_ERROR_MCAST) {
1125 dhcpv6_request(dhcp, true);
1128 dhcpv6_rebind(dhcp);
1134 } else if (status == G_DHCPV6_ERROR_LINK) {
1135 if (req_type == REQ_REQUEST) {
1136 g_dhcp_client_unref(dhcp->dhcp_client);
1137 start_solicitation(dhcp);
1140 dhcp->callback(dhcp->network,
1141 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1143 } else if (status == G_DHCPV6_ERROR_FAILURE) {
1144 if (req_type == REQ_REQUEST) {
1145 /* Rate limit the resend of request message */
1146 do_resend_request(dhcp);
1149 dhcp->callback(dhcp->network,
1150 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1155 * If we did not got any addresses, then re-send
1160 option = g_dhcp_client_get_option(dhcp->dhcp_client,
1163 option = g_dhcp_client_get_option(dhcp->dhcp_client,
1168 do_resend_request(dhcp);
1171 dhcpv6_rebind(dhcp);
1181 if (status == G_DHCPV6_ERROR_SUCCESS)
1182 do_dad(dhcp_client, dhcp);
1183 else if (dhcp->callback)
1184 dhcp->callback(dhcp->network,
1185 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1189 static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data)
1193 g_dhcpv6_client_reset_request(dhcp_client);
1194 g_dhcpv6_client_clear_retransmit(dhcp_client);
1196 re_cb(REQ_REBIND, dhcp_client, user_data);
1199 static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp)
1201 GDHCPClient *dhcp_client;
1203 DBG("dhcp %p", dhcp);
1205 dhcp_client = dhcp->dhcp_client;
1207 g_dhcp_client_clear_requests(dhcp_client);
1209 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1210 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1211 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1212 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1214 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1215 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1217 g_dhcpv6_client_set_ia(dhcp_client,
1218 connman_network_get_index(dhcp->network),
1219 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1220 NULL, NULL, TRUE, NULL);
1222 clear_callbacks(dhcp_client);
1224 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
1227 dhcp->dhcp_client = dhcp_client;
1229 return g_dhcp_client_start(dhcp_client, NULL);
1232 static gboolean dhcpv6_restart(gpointer user_data)
1234 struct connman_dhcpv6 *dhcp = user_data;
1237 dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
1244 * Check if we need to restart the solicitation procedure. This
1245 * is done if all the addresses have expired. RFC 3315, 18.1.4
1247 static int check_restart(struct connman_dhcpv6 *dhcp)
1249 time_t current, expired;
1251 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
1254 /* infinite lifetime for an DHCPv6 address */
1255 if (expired == 0xffffffff)
1258 current = time(NULL);
1260 if (current >= expired) {
1261 DBG("expired by %d secs", (int)(current - expired));
1263 g_idle_add(dhcpv6_restart, dhcp);
1271 static gboolean timeout_rebind(gpointer user_data)
1273 struct connman_dhcpv6 *dhcp = user_data;
1275 if (check_restart(dhcp) < 0)
1278 dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
1280 DBG("rebind RT timeout %d msec", dhcp->RT);
1282 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1284 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1286 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1291 static gboolean start_rebind(gpointer user_data)
1293 struct connman_dhcpv6 *dhcp = user_data;
1295 if (check_restart(dhcp) < 0)
1298 dhcp->RT = initial_rt(REB_TIMEOUT);
1300 DBG("rebind initial RT timeout %d msec", dhcp->RT);
1302 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1304 dhcpv6_rebind(dhcp);
1309 static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
1313 g_dhcpv6_client_reset_request(dhcp_client);
1314 g_dhcpv6_client_clear_retransmit(dhcp_client);
1316 re_cb(REQ_REQUEST, dhcp_client, user_data);
1319 static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
1322 GDHCPClient *dhcp_client;
1325 DBG("dhcp %p add %d", dhcp, add_addresses);
1327 dhcp_client = dhcp->dhcp_client;
1329 g_dhcp_client_clear_requests(dhcp_client);
1331 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1332 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1333 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1334 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1335 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1337 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1338 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1340 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1341 g_dhcpv6_client_set_ia(dhcp_client,
1342 connman_network_get_index(dhcp->network),
1343 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1344 &T1, &T2, add_addresses, NULL);
1346 clear_callbacks(dhcp_client);
1348 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
1351 dhcp->dhcp_client = dhcp_client;
1353 return g_dhcp_client_start(dhcp_client, NULL);
1356 static gboolean timeout_request(gpointer user_data)
1358 struct connman_dhcpv6 *dhcp = user_data;
1360 if (dhcp->request_count >= REQ_MAX_RC) {
1361 DBG("max request retry attempts %d", dhcp->request_count);
1362 dhcp->request_count = 0;
1364 dhcp->callback(dhcp->network,
1365 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1369 dhcp->request_count++;
1371 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1372 DBG("request RT timeout %d msec", dhcp->RT);
1373 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1375 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1377 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1382 static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
1386 g_dhcpv6_client_reset_request(dhcp_client);
1387 g_dhcpv6_client_clear_retransmit(dhcp_client);
1389 re_cb(REQ_RENEW, dhcp_client, user_data);
1392 static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
1394 GDHCPClient *dhcp_client;
1397 DBG("dhcp %p", dhcp);
1399 dhcp_client = dhcp->dhcp_client;
1401 g_dhcp_client_clear_requests(dhcp_client);
1403 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1404 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1405 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1406 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1407 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1409 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1410 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1412 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1413 g_dhcpv6_client_set_ia(dhcp_client,
1414 connman_network_get_index(dhcp->network),
1415 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1416 &T1, &T2, TRUE, NULL);
1418 clear_callbacks(dhcp_client);
1420 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
1423 dhcp->dhcp_client = dhcp_client;
1425 return g_dhcp_client_start(dhcp_client, NULL);
1428 static gboolean timeout_renew(gpointer user_data)
1430 struct connman_dhcpv6 *dhcp = user_data;
1431 time_t last_rebind, current;
1434 if (check_restart(dhcp) < 0)
1437 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, &T2,
1438 &last_rebind, NULL);
1439 current = time(NULL);
1440 if ((unsigned)current >= (unsigned)last_rebind + T2) {
1442 * Do rebind instead if past T2
1448 dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
1450 DBG("renew RT timeout %d msec", dhcp->RT);
1452 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1454 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1456 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1461 static gboolean start_renew(gpointer user_data)
1463 struct connman_dhcpv6 *dhcp = user_data;
1465 dhcp->RT = initial_rt(REN_TIMEOUT);
1467 DBG("renew initial RT timeout %d msec", dhcp->RT);
1469 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1476 int __connman_dhcpv6_start_renew(struct connman_network *network,
1479 struct connman_dhcpv6 *dhcp;
1480 uint32_t T1, T2, delta;
1481 time_t started, current, expired;
1483 dhcp = g_hash_table_lookup(network_table, network);
1487 DBG("network %p dhcp %p", network, dhcp);
1491 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
1492 &started, &expired);
1494 current = time(NULL);
1496 DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
1497 (unsigned long)expired, current, started);
1499 if (T1 == 0xffffffff)
1500 /* RFC 3315, 22.4 */
1505 * Client can choose the timeout.
1507 T1 = (expired - started) / 2;
1508 T2 = (expired - started) / 10 * 8;
1511 dhcp->callback = callback;
1513 /* RFC 3315, 18.1.4, start solicit if expired */
1514 if (check_restart(dhcp) < 0)
1517 if (T2 != 0xffffffff && T2 > 0) {
1518 if ((unsigned)current >= (unsigned)started + T2) {
1519 /* RFC 3315, chapter 18.1.3, start rebind */
1520 DBG("start rebind immediately");
1522 dhcp->timeout = g_idle_add(start_rebind, dhcp);
1524 } else if ((unsigned)current < (unsigned)started + T1) {
1525 delta = started + T1 - current;
1526 DBG("renew after %d secs", delta);
1528 dhcp->timeout = g_timeout_add_seconds(delta,
1531 delta = started + T2 - current;
1532 DBG("rebind after %d secs", delta);
1534 dhcp->timeout = g_timeout_add_seconds(delta,
1535 start_rebind, dhcp);
1542 static void release_cb(GDHCPClient *dhcp_client, gpointer user_data)
1547 int __connman_dhcpv6_start_release(struct connman_network *network,
1550 struct connman_dhcpv6 *dhcp;
1551 GDHCPClient *dhcp_client;
1554 return 0; /* we are already released */
1556 dhcp = g_hash_table_lookup(network_table, network);
1560 DBG("network %p dhcp %p client %p stateless %d", network, dhcp,
1561 dhcp->dhcp_client, dhcp->stateless);
1563 if (dhcp->stateless)
1568 dhcp_client = dhcp->dhcp_client;
1571 * We had started the DHCPv6 handshaking i.e., we have called
1572 * __connman_dhcpv6_start() but it has not yet sent
1573 * a solicitation message to server. This means that we do not
1574 * have DHCPv6 configured yet so we can just quit here.
1576 DBG("DHCPv6 was not started");
1580 g_dhcp_client_clear_requests(dhcp_client);
1581 g_dhcp_client_clear_values(dhcp_client);
1583 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1584 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1586 g_dhcpv6_client_set_ia(dhcp_client,
1587 connman_network_get_index(dhcp->network),
1588 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1589 NULL, NULL, TRUE, NULL);
1591 clear_callbacks(dhcp_client);
1594 * Although we register a callback here we are really not interested in
1595 * the answer because it might take too long time and network code
1596 * might be in the middle of the disconnect.
1597 * So we just inform the server that we are done with the addresses
1598 * but ignore the reply from server. This is allowed by RFC 3315
1601 g_dhcp_client_register_event(dhcp_client,
1602 G_DHCP_CLIENT_EVENT_RELEASE,
1605 dhcp->dhcp_client = dhcp_client;
1607 return g_dhcp_client_start(dhcp_client, NULL);
1610 static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
1612 DBG("dhcp %p", dhcp);
1618 if (!dhcp->dhcp_client)
1621 g_dhcp_client_stop(dhcp->dhcp_client);
1622 g_dhcp_client_unref(dhcp->dhcp_client);
1624 dhcp->dhcp_client = NULL;
1629 static void remove_network(gpointer user_data)
1631 struct connman_dhcpv6 *dhcp = user_data;
1633 DBG("dhcp %p", dhcp);
1635 dhcpv6_release(dhcp);
1640 static gboolean timeout_info_req(gpointer user_data)
1642 struct connman_dhcpv6 *dhcp = user_data;
1644 dhcp->RT = calc_delay(dhcp->RT, INF_MAX_RT);
1646 DBG("info RT timeout %d msec", dhcp->RT);
1648 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1650 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1652 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1657 static gboolean start_info_req(gpointer user_data)
1659 struct connman_dhcpv6 *dhcp = user_data;
1661 /* Set the retransmission timeout, RFC 3315 chapter 14 */
1662 dhcp->RT = initial_rt(INF_TIMEOUT);
1664 DBG("info initial RT timeout %d msec", dhcp->RT);
1666 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1668 dhcpv6_info_request(dhcp);
1673 int __connman_dhcpv6_start_info(struct connman_network *network,
1676 struct connman_dhcpv6 *dhcp;
1682 if (network_table) {
1683 dhcp = g_hash_table_lookup(network_table, network);
1684 if (dhcp && dhcp->started)
1688 dhcp = g_try_new0(struct connman_dhcpv6, 1);
1692 dhcp->network = network;
1693 dhcp->callback = callback;
1694 dhcp->stateless = true;
1695 dhcp->started = true;
1697 connman_network_ref(network);
1699 DBG("replace network %p dhcp %p", network, dhcp);
1701 g_hash_table_replace(network_table, network, dhcp);
1703 /* Initial timeout, RFC 3315, 18.1.5 */
1704 __connman_util_get_random(&rand);
1705 delay = rand % 1000;
1707 dhcp->timeout = g_timeout_add(delay, start_info_req, dhcp);
1712 static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data)
1714 struct connman_dhcpv6 *dhcp = user_data;
1716 DBG("dhcpv6 advertise msg %p", dhcp);
1720 g_dhcpv6_client_clear_retransmit(dhcp_client);
1722 if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1724 dhcp->callback(dhcp->network,
1725 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1729 dhcp->RT = initial_rt(REQ_TIMEOUT);
1730 DBG("request initial RT timeout %d msec", dhcp->RT);
1731 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1733 dhcp->request_count = 1;
1735 dhcpv6_request(dhcp, true);
1738 static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)
1740 /* We get here if server supports rapid commit */
1741 struct connman_dhcpv6 *dhcp = user_data;
1743 DBG("dhcpv6 solicitation msg %p", dhcp);
1747 g_dhcpv6_client_clear_retransmit(dhcp_client);
1749 if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1751 dhcp->callback(dhcp->network,
1752 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1756 do_dad(dhcp_client, dhcp);
1759 static gboolean timeout_solicitation(gpointer user_data)
1761 struct connman_dhcpv6 *dhcp = user_data;
1763 dhcp->RT = calc_delay(dhcp->RT, SOL_MAX_RT);
1765 DBG("solicit RT timeout %d msec", dhcp->RT);
1767 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1769 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1771 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1776 static int dhcpv6_solicitation(struct connman_dhcpv6 *dhcp)
1778 struct connman_service *service;
1779 #if !defined TIZEN_EXT
1780 struct connman_ipconfig *ipconfig_ipv6;
1782 GDHCPClient *dhcp_client;
1783 GDHCPClientError error;
1786 DBG("dhcp %p", dhcp);
1788 index = connman_network_get_index(dhcp->network);
1790 dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
1791 if (error != G_DHCP_CLIENT_ERROR_NONE) {
1796 #if !defined TIZEN_EXT
1797 if (getenv("CONNMAN_DHCPV6_DEBUG"))
1799 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
1801 service = connman_service_lookup_from_network(dhcp->network);
1804 g_dhcp_client_unref(dhcp_client);
1808 ret = set_duid(service, dhcp->network, dhcp_client, index);
1811 g_dhcp_client_unref(dhcp_client);
1815 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1816 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
1817 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1818 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1819 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1821 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1822 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1824 #if defined TIZEN_EXT
1826 When privacy extension is enabled then connman requests
1827 OPTION_IA_TA (4) from DHCPv6 server. This option is used to request
1828 temporary IPv6 address from DHCPv6 server but we found that DHCPv6
1829 server never provided temporary IPv6 address and connman resend dhcpv6
1830 requests. So always set OPTION_IA_NA in dhcpv6 request to get IPv6
1831 address from DHCPv6 server.
1833 dhcp->use_ta = FALSE;
1835 ipconfig_ipv6 = __connman_service_get_ip6config(service);
1836 dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
1839 g_dhcpv6_client_set_ia(dhcp_client, index,
1840 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1841 NULL, NULL, FALSE, NULL);
1843 clear_callbacks(dhcp_client);
1845 g_dhcp_client_register_event(dhcp_client,
1846 G_DHCP_CLIENT_EVENT_SOLICITATION,
1847 solicitation_cb, dhcp);
1849 g_dhcp_client_register_event(dhcp_client,
1850 G_DHCP_CLIENT_EVENT_ADVERTISE,
1851 advertise_cb, dhcp);
1853 dhcp->dhcp_client = dhcp_client;
1855 return g_dhcp_client_start(dhcp_client, NULL);
1858 static gboolean start_solicitation(gpointer user_data)
1860 struct connman_dhcpv6 *dhcp = user_data;
1862 /* Set the retransmission timeout, RFC 3315 chapter 14 */
1863 dhcp->RT = initial_rt(SOL_TIMEOUT);
1865 DBG("solicit initial RT timeout %d msec", dhcp->RT);
1867 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1869 dhcpv6_solicitation(dhcp);
1874 int __connman_dhcpv6_start(struct connman_network *network,
1875 GSList *prefixes, dhcpv6_cb callback)
1877 struct connman_service *service;
1878 struct connman_dhcpv6 *dhcp;
1884 if (network_table) {
1885 dhcp = g_hash_table_lookup(network_table, network);
1886 if (dhcp && dhcp->started)
1890 service = connman_service_lookup_from_network(network);
1894 dhcp = g_try_new0(struct connman_dhcpv6, 1);
1898 dhcp->network = network;
1899 dhcp->callback = callback;
1900 dhcp->prefixes = prefixes;
1901 dhcp->started = true;
1903 connman_network_ref(network);
1905 DBG("replace network %p dhcp %p", network, dhcp);
1907 g_hash_table_replace(network_table, network, dhcp);
1909 /* Initial timeout, RFC 3315, 17.1.2 */
1910 __connman_util_get_random(&rand);
1911 delay = rand % 1000;
1914 * Start from scratch.
1915 * RFC 3315, chapter 17.1.2 Solicitation message
1917 * Note that we do not send CONFIRM message here as it does
1918 * not make much sense because we do not save expiration time
1919 * so we cannot really know how long the saved address is valid
1920 * anyway. The reply to CONFIRM message does not send
1921 * expiration times back to us. Because of this we need to
1922 * start using SOLICITATION anyway.
1924 dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
1929 void __connman_dhcpv6_stop(struct connman_network *network)
1936 if (g_hash_table_remove(network_table, network))
1937 connman_network_unref(network);
1940 static int save_prefixes(struct connman_ipconfig *ipconfig,
1944 int i = 0, count = g_slist_length(prefixes);
1950 array = g_try_new0(char *, count + 1);
1954 for (list = prefixes; list; list = list->next) {
1955 char *elem, addr_str[INET6_ADDRSTRLEN];
1956 GDHCPIAPrefix *prefix = list->data;
1958 elem = g_strdup_printf("%s/%d", inet_ntop(AF_INET6,
1959 &prefix->prefix, addr_str, INET6_ADDRSTRLEN),
1969 __connman_ipconfig_set_dhcpv6_prefixes(ipconfig, array);
1973 static GSList *load_prefixes(struct connman_ipconfig *ipconfig)
1976 GSList *list = NULL;
1977 char **array = __connman_ipconfig_get_dhcpv6_prefixes(ipconfig);
1982 for (i = 0; array[i]; i++) {
1983 GDHCPIAPrefix *prefix;
1985 char *ptr, **elems = g_strsplit(array[i], "/", 0);
1990 value = strtol(elems[1], &ptr, 10);
1991 if (ptr != elems[1] && *ptr == '\0' && value <= 128) {
1992 struct in6_addr addr;
1994 if (inet_pton(AF_INET6, elems[0], &addr) == 1) {
1995 prefix = g_try_new0(GDHCPIAPrefix, 1);
2000 memcpy(&prefix->prefix, &addr,
2001 sizeof(struct in6_addr));
2002 prefix->prefixlen = value;
2004 list = g_slist_prepend(list, prefix);
2014 static GDHCPIAPrefix *copy_prefix(gpointer data)
2016 GDHCPIAPrefix *copy, *prefix = data;
2018 copy = g_try_new(GDHCPIAPrefix, 1);
2022 memcpy(copy, prefix, sizeof(GDHCPIAPrefix));
2027 static GSList *copy_and_convert_prefixes(GList *prefixes)
2029 GSList *copy = NULL;
2032 for (list = prefixes; list; list = list->next)
2033 copy = g_slist_prepend(copy, copy_prefix(list->data));
2038 static int set_prefixes(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
2041 g_slist_free_full(dhcp->prefixes, free_prefix);
2044 copy_and_convert_prefixes(g_dhcp_client_get_option(dhcp_client,
2047 DBG("Got %d prefix", g_slist_length(dhcp->prefixes));
2049 if (dhcp->callback) {
2050 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
2051 if (status == G_DHCPV6_ERROR_NO_PREFIX)
2052 dhcp->callback(dhcp->network,
2053 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2055 struct connman_service *service;
2056 struct connman_ipconfig *ipconfig;
2057 int ifindex = connman_network_get_index(dhcp->network);
2059 service = __connman_service_lookup_from_index(ifindex);
2061 ipconfig = __connman_service_get_ip6config(
2063 save_prefixes(ipconfig, dhcp->prefixes);
2064 __connman_service_save(service);
2067 dhcp->callback(dhcp->network,
2068 CONNMAN_DHCPV6_STATUS_SUCCEED, dhcp->prefixes);
2071 g_slist_free_full(dhcp->prefixes, free_prefix);
2072 dhcp->prefixes = NULL;
2078 static void re_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2080 struct connman_dhcpv6 *dhcp = user_data;
2084 status = g_dhcpv6_client_get_status(dhcp_client);
2086 DBG("dhcpv6 cb msg %p status %d", dhcp, status);
2088 if (status == G_DHCPV6_ERROR_BINDING) {
2089 /* RFC 3315, 18.1.8 */
2090 dhcpv6_pd_request(dhcp);
2092 ret = set_prefixes(dhcp_client, dhcp);
2095 dhcp->callback(dhcp->network,
2096 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2102 static void rebind_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2106 g_dhcpv6_client_clear_retransmit(dhcp_client);
2108 re_pd_cb(dhcp_client, user_data);
2111 static GDHCPClient *create_pd_client(struct connman_dhcpv6 *dhcp, int *err)
2113 GDHCPClient *dhcp_client;
2114 GDHCPClientError error;
2115 struct connman_service *service;
2119 index = connman_network_get_index(dhcp->network);
2121 dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
2122 if (error != G_DHCP_CLIENT_ERROR_NONE) {
2127 #if !defined TIZEN_EXT
2128 if (getenv("CONNMAN_DHCPV6_DEBUG"))
2130 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6:PD");
2132 service = connman_service_lookup_from_network(dhcp->network);
2134 g_dhcp_client_unref(dhcp_client);
2139 ret = set_duid(service, dhcp->network, dhcp_client, index);
2141 g_dhcp_client_unref(dhcp_client);
2146 g_dhcpv6_client_create_iaid(dhcp_client, index, (unsigned char *)&iaid);
2147 g_dhcpv6_client_set_iaid(dhcp_client, iaid);
2152 static int dhcpv6_pd_rebind(struct connman_dhcpv6 *dhcp)
2154 GDHCPClient *dhcp_client;
2157 DBG("dhcp %p", dhcp);
2159 if (!dhcp->dhcp_client) {
2161 * We skipped the solicitation phase
2165 dhcp->dhcp_client = create_pd_client(dhcp, &err);
2166 if (!dhcp->dhcp_client) {
2172 dhcp_client = dhcp->dhcp_client;
2174 g_dhcp_client_clear_requests(dhcp_client);
2176 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2177 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2178 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2180 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2181 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2183 clear_callbacks(dhcp_client);
2185 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
2186 rebind_pd_cb, dhcp);
2188 return g_dhcp_client_start(dhcp_client, NULL);
2191 static void renew_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2195 g_dhcpv6_client_clear_retransmit(dhcp_client);
2197 re_pd_cb(dhcp_client, user_data);
2200 static int dhcpv6_pd_renew(struct connman_dhcpv6 *dhcp)
2202 GDHCPClient *dhcp_client;
2205 DBG("dhcp %p", dhcp);
2207 dhcp_client = dhcp->dhcp_client;
2209 g_dhcp_client_clear_requests(dhcp_client);
2211 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2212 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2213 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2214 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2216 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2217 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2219 clear_callbacks(dhcp_client);
2221 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
2224 return g_dhcp_client_start(dhcp_client, NULL);
2228 * Check if we need to restart the solicitation procedure. This
2229 * is done if all the prefixes have expired.
2231 static int check_pd_restart(struct connman_dhcpv6 *dhcp)
2233 time_t current, expired;
2235 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
2237 current = time(NULL);
2239 if (current > expired) {
2240 DBG("expired by %d secs", (int)(current - expired));
2242 g_idle_add(dhcpv6_restart, dhcp);
2250 static gboolean timeout_pd_rebind(gpointer user_data)
2252 struct connman_dhcpv6 *dhcp = user_data;
2254 if (check_pd_restart(dhcp) < 0)
2257 dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
2259 DBG("rebind RT timeout %d msec", dhcp->RT);
2261 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2263 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2265 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2270 static gboolean start_pd_rebind(gpointer user_data)
2272 struct connman_dhcpv6 *dhcp = user_data;
2274 if (check_pd_restart(dhcp) < 0)
2277 dhcp->RT = initial_rt(REB_TIMEOUT);
2279 DBG("rebind initial RT timeout %d msec", dhcp->RT);
2281 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2283 dhcpv6_pd_rebind(dhcp);
2288 static gboolean timeout_pd_rebind_confirm(gpointer user_data)
2290 struct connman_dhcpv6 *dhcp = user_data;
2292 dhcp->RT = calc_delay(dhcp->RT, CNF_MAX_RT);
2294 DBG("rebind with confirm RT timeout %d msec", dhcp->RT);
2296 dhcp->timeout = g_timeout_add(dhcp->RT,
2297 timeout_pd_rebind_confirm, dhcp);
2299 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2301 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2306 static gboolean timeout_pd_max_confirm(gpointer user_data)
2308 struct connman_dhcpv6 *dhcp = user_data;
2314 DBG("rebind with confirm max retransmit duration timeout");
2316 g_dhcpv6_client_clear_retransmit(dhcp->dhcp_client);
2319 dhcp->callback(dhcp->network,
2320 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2325 static gboolean start_pd_rebind_with_confirm(gpointer user_data)
2327 struct connman_dhcpv6 *dhcp = user_data;
2329 dhcp->RT = initial_rt(CNF_TIMEOUT);
2331 DBG("rebind with confirm initial RT timeout %d msec", dhcp->RT);
2333 dhcp->timeout = g_timeout_add(dhcp->RT,
2334 timeout_pd_rebind_confirm, dhcp);
2335 dhcp->MRD = g_timeout_add(CNF_MAX_RD, timeout_pd_max_confirm, dhcp);
2337 dhcpv6_pd_rebind(dhcp);
2342 static gboolean timeout_pd_renew(gpointer user_data)
2344 struct connman_dhcpv6 *dhcp = user_data;
2346 if (check_pd_restart(dhcp) < 0)
2349 dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
2351 DBG("renew RT timeout %d msec", dhcp->RT);
2353 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
2355 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2357 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2362 static gboolean start_pd_renew(gpointer user_data)
2364 struct connman_dhcpv6 *dhcp = user_data;
2366 dhcp->RT = initial_rt(REN_TIMEOUT);
2368 DBG("renew initial RT timeout %d msec", dhcp->RT);
2370 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_renew, dhcp);
2372 dhcpv6_pd_renew(dhcp);
2377 int __connman_dhcpv6_start_pd_renew(struct connman_network *network,
2380 struct connman_dhcpv6 *dhcp;
2382 time_t started, current, expired;
2384 dhcp = g_hash_table_lookup(network_pd_table, network);
2388 DBG("network %p dhcp %p", network, dhcp);
2392 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
2393 &started, &expired);
2395 current = time(NULL);
2397 DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
2398 expired, current, started);
2400 if (T1 == 0xffffffff)
2401 /* RFC 3633, ch 9 */
2406 * Client can choose the timeout.
2410 dhcp->callback = callback;
2412 /* RFC 3315, 18.1.4, start solicit if expired */
2413 if (check_pd_restart(dhcp) < 0)
2416 if (T2 != 0xffffffff && T2 > 0) {
2417 if ((unsigned)current >= (unsigned)started + T2) {
2418 /* RFC 3315, chapter 18.1.3, start rebind */
2419 DBG("rebind after %d secs", T2);
2421 dhcp->timeout = g_timeout_add_seconds(T2,
2425 } else if ((unsigned)current < (unsigned)started + T1) {
2426 DBG("renew after %d secs", T1);
2428 dhcp->timeout = g_timeout_add_seconds(T1,
2432 DBG("rebind after %d secs", T2 - T1);
2434 dhcp->timeout = g_timeout_add_seconds(T2 - T1,
2443 static void release_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2448 int __connman_dhcpv6_start_pd_release(struct connman_network *network,
2451 struct connman_dhcpv6 *dhcp;
2452 GDHCPClient *dhcp_client;
2456 return 0; /* we are already released */
2458 dhcp = g_hash_table_lookup(network_pd_table, network);
2462 DBG("network %p dhcp %p client %p", network, dhcp, dhcp->dhcp_client);
2466 dhcp_client = dhcp->dhcp_client;
2468 DBG("DHCPv6 PD was not started");
2472 g_dhcp_client_clear_requests(dhcp_client);
2473 g_dhcp_client_clear_values(dhcp_client);
2475 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2476 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2478 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2479 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2481 clear_callbacks(dhcp_client);
2483 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RELEASE,
2484 release_pd_cb, dhcp);
2486 dhcp->dhcp_client = dhcp_client;
2488 return g_dhcp_client_start(dhcp_client, NULL);
2491 static gboolean timeout_pd_request(gpointer user_data)
2493 struct connman_dhcpv6 *dhcp = user_data;
2495 if (dhcp->request_count >= REQ_MAX_RC) {
2496 DBG("max request retry attempts %d", dhcp->request_count);
2497 dhcp->request_count = 0;
2499 dhcp->callback(dhcp->network,
2500 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2504 dhcp->request_count++;
2506 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
2507 DBG("request RT timeout %d msec", dhcp->RT);
2508 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2510 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2512 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2517 static void request_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2519 struct connman_dhcpv6 *dhcp = user_data;
2524 g_dhcpv6_client_clear_retransmit(dhcp_client);
2526 status = g_dhcpv6_client_get_status(dhcp_client);
2528 DBG("dhcpv6 pd cb msg %p status %d", dhcp, status);
2530 if (status == G_DHCPV6_ERROR_BINDING) {
2531 /* RFC 3315, 18.1.8 */
2532 dhcpv6_pd_request(dhcp);
2534 set_prefixes(dhcp_client, dhcp);
2538 static int dhcpv6_pd_request(struct connman_dhcpv6 *dhcp)
2540 GDHCPClient *dhcp_client;
2541 uint32_t T1 = 0, T2 = 0;
2543 DBG("dhcp %p", dhcp);
2545 dhcp_client = dhcp->dhcp_client;
2547 g_dhcp_client_clear_requests(dhcp_client);
2549 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2550 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2551 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2552 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2554 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2555 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2557 clear_callbacks(dhcp_client);
2559 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
2560 request_pd_cb, dhcp);
2562 return g_dhcp_client_start(dhcp_client, NULL);
2565 static void advertise_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2567 struct connman_dhcpv6 *dhcp = user_data;
2569 DBG("dhcpv6 advertise pd msg %p", dhcp);
2573 g_dhcpv6_client_clear_retransmit(dhcp_client);
2575 if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
2577 dhcp->callback(dhcp->network,
2578 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2582 dhcp->RT = initial_rt(REQ_TIMEOUT);
2583 DBG("request initial RT timeout %d msec", dhcp->RT);
2584 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2586 dhcp->request_count = 1;
2588 dhcpv6_pd_request(dhcp);
2591 static void solicitation_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2594 * This callback is here so that g_dhcp_client_start()
2595 * will enter the proper L3 mode.
2597 DBG("DHCPv6 %p solicitation msg received, ignoring it", user_data);
2600 static int dhcpv6_pd_solicitation(struct connman_dhcpv6 *dhcp)
2602 GDHCPClient *dhcp_client;
2605 DBG("dhcp %p", dhcp);
2607 dhcp_client = create_pd_client(dhcp, &ret);
2613 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2615 g_dhcpv6_client_set_pd(dhcp_client, NULL, NULL, NULL);
2617 clear_callbacks(dhcp_client);
2619 g_dhcp_client_register_event(dhcp_client,
2620 G_DHCP_CLIENT_EVENT_ADVERTISE,
2621 advertise_pd_cb, dhcp);
2623 g_dhcp_client_register_event(dhcp_client,
2624 G_DHCP_CLIENT_EVENT_SOLICITATION,
2625 solicitation_pd_cb, dhcp);
2627 dhcp->dhcp_client = dhcp_client;
2629 return g_dhcp_client_start(dhcp_client, NULL);
2632 static gboolean start_pd_solicitation(gpointer user_data)
2634 struct connman_dhcpv6 *dhcp = user_data;
2636 /* Set the retransmission timeout, RFC 3315 chapter 14 */
2637 dhcp->RT = initial_rt(SOL_TIMEOUT);
2639 DBG("solicit initial RT timeout %d msec", dhcp->RT);
2641 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
2643 dhcpv6_pd_solicitation(dhcp);
2648 int __connman_dhcpv6_start_pd(int index, GSList *prefixes, dhcpv6_cb callback)
2650 struct connman_service *service;
2651 struct connman_network *network;
2652 struct connman_dhcpv6 *dhcp;
2657 DBG("index %d", index);
2659 service = __connman_service_lookup_from_index(index);
2663 network = __connman_service_get_network(service);
2667 if (network_pd_table) {
2668 dhcp = g_hash_table_lookup(network_pd_table, network);
2669 if (dhcp && dhcp->started)
2673 dhcp = g_try_new0(struct connman_dhcpv6, 1);
2677 dhcp->network = network;
2678 dhcp->callback = callback;
2679 dhcp->started = true;
2683 * Try to load the earlier prefixes if caller did not supply
2684 * any that we could use.
2686 struct connman_ipconfig *ipconfig;
2687 ipconfig = __connman_service_get_ip6config(service);
2689 dhcp->prefixes = load_prefixes(ipconfig);
2691 dhcp->prefixes = prefixes;
2693 connman_network_ref(network);
2695 DBG("replace network %p dhcp %p", network, dhcp);
2697 g_hash_table_replace(network_pd_table, network, dhcp);
2699 if (!dhcp->prefixes) {
2701 * Refresh start, try to get prefixes.
2703 start_pd_solicitation(dhcp);
2706 * We used to have prefixes, try to use them again.
2707 * We need to use timeouts from confirm msg, RFC 3633, ch 12.1
2709 start_pd_rebind_with_confirm(dhcp);
2715 void __connman_dhcpv6_stop_pd(int index)
2717 struct connman_service *service;
2718 struct connman_network *network;
2723 DBG("index %d", index);
2725 if (!network_pd_table)
2728 service = __connman_service_lookup_from_index(index);
2732 network = __connman_service_get_network(service);
2736 __connman_dhcpv6_start_pd_release(network, NULL);
2738 if (g_hash_table_remove(network_pd_table, network))
2739 connman_network_unref(network);
2742 int __connman_dhcpv6_init(void)
2746 network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2747 NULL, remove_network);
2749 network_pd_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2750 NULL, remove_network);
2755 void __connman_dhcpv6_cleanup(void)
2759 g_hash_table_destroy(network_table);
2760 network_table = NULL;
2762 g_hash_table_destroy(network_pd_table);
2763 network_pd_table = NULL;