5 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31 #include <connman/ipconfig.h>
32 #include <connman/storage.h>
34 #include <gdhcp/gdhcp.h>
40 /* Transmission params in msec, RFC 3315 chapter 5.5 */
41 #define INF_MAX_DELAY (1 * 1000)
42 #define INF_TIMEOUT (1 * 1000)
43 #define INF_MAX_RT (120 * 1000)
44 #define SOL_MAX_DELAY (1 * 1000)
45 #define SOL_TIMEOUT (1 * 1000)
46 #define SOL_MAX_RT (120 * 1000)
47 #define REQ_TIMEOUT (1 * 1000)
48 #define REQ_MAX_RT (30 * 1000)
50 #define REN_TIMEOUT (10 * 1000)
51 #define REN_MAX_RT (600 * 1000)
52 #define REB_TIMEOUT (10 * 1000)
53 #define REB_MAX_RT (600 * 1000)
54 #define CNF_MAX_DELAY (1 * 1000)
55 #define CNF_TIMEOUT (1 * 1000)
56 #define CNF_MAX_RT (4 * 1000)
57 #define CNF_MAX_RD (10 * 1000)
58 #define DEC_TIMEOUT (1 * 1000)
67 struct connman_dhcpv6 {
68 struct connman_network *network;
74 GDHCPClient *dhcp_client;
76 guint timeout; /* operation timeout in msec */
77 guint MRD; /* max operation timeout in msec */
78 guint RT; /* in msec */
79 bool use_ta; /* set to TRUE if IPv6 privacy is enabled */
80 GSList *prefixes; /* network prefixes from radvd or dhcpv6 pd */
81 int request_count; /* how many times REQUEST have been sent */
82 bool stateless; /* TRUE if stateless DHCPv6 is used */
83 bool started; /* TRUE if we have DHCPv6 started */
86 static GHashTable *network_table;
87 static GHashTable *network_pd_table;
89 static int dhcpv6_request(struct connman_dhcpv6 *dhcp, bool add_addresses);
90 static int dhcpv6_pd_request(struct connman_dhcpv6 *dhcp);
91 static gboolean start_solicitation(gpointer user_data);
92 static int dhcpv6_renew(struct connman_dhcpv6 *dhcp);
93 static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp);
95 static void clear_timer(struct connman_dhcpv6 *dhcp)
97 if (dhcp->timeout > 0) {
98 g_source_remove(dhcp->timeout);
103 g_source_remove(dhcp->MRD);
108 static inline float get_random(void)
110 return (rand() % 200 - 100) / 1000.0;
113 /* Calculate a random delay, RFC 3315 chapter 14 */
114 /* RT and MRT are milliseconds */
115 static guint calc_delay(guint RT, guint MRT)
117 float delay = get_random();
118 float rt = RT * (2 + delay);
121 rt = MRT * (1 + delay);
129 static void free_prefix(gpointer data)
134 static void dhcpv6_free(struct connman_dhcpv6 *dhcp)
136 g_strfreev(dhcp->nameservers);
137 g_strfreev(dhcp->timeservers);
139 dhcp->nameservers = NULL;
140 dhcp->timeservers = NULL;
141 dhcp->started = false;
143 g_slist_free_full(dhcp->prefixes, free_prefix);
146 static bool compare_string_arrays(char **array_a, char **array_b)
150 if (!array_a || !array_b)
153 if (g_strv_length(array_a) != g_strv_length(array_b))
156 for (i = 0; array_a[i] && array_b[i]; i++)
157 if (g_strcmp0(array_a[i], array_b[i]) != 0)
163 static void dhcpv6_debug(const char *str, void *data)
165 connman_info("%s: %s\n", (const char *) data, str);
168 static gchar *convert_to_hex(unsigned char *buf, int len)
170 gchar *ret = g_try_malloc(len * 2 + 1);
173 for (i = 0; ret && i < len; i++)
174 g_snprintf(ret + i * 2, 3, "%02x", buf[i]);
180 * DUID should not change over time so save it to file.
181 * See RFC 3315 chapter 9 for details.
183 static int set_duid(struct connman_service *service,
184 struct connman_network *network,
185 GDHCPClient *dhcp_client, int index)
193 ident = __connman_service_get_ident(service);
195 keyfile = connman_storage_load_service(ident);
199 hex_duid = g_key_file_get_string(keyfile, ident, "IPv6.DHCP.DUID",
202 unsigned int i, j = 0, hex;
203 size_t hex_duid_len = strlen(hex_duid);
205 duid = g_try_malloc0(hex_duid_len / 2);
207 g_key_file_free(keyfile);
212 for (i = 0; i < hex_duid_len; i += 2) {
213 sscanf(hex_duid + i, "%02x", &hex);
217 duid_len = hex_duid_len / 2;
220 int type = __connman_ipconfig_get_type_from_index(index);
222 ret = g_dhcpv6_create_duid(G_DHCPV6_DUID_LLT, index, type,
225 g_key_file_free(keyfile);
229 hex_duid = convert_to_hex(duid, duid_len);
231 g_key_file_free(keyfile);
235 g_key_file_set_string(keyfile, ident, "IPv6.DHCP.DUID",
238 __connman_storage_save_service(keyfile, ident);
242 g_key_file_free(keyfile);
244 g_dhcpv6_client_set_duid(dhcp_client, duid, duid_len);
249 static void clear_callbacks(GDHCPClient *dhcp_client)
251 g_dhcp_client_register_event(dhcp_client,
252 G_DHCP_CLIENT_EVENT_SOLICITATION,
255 g_dhcp_client_register_event(dhcp_client,
256 G_DHCP_CLIENT_EVENT_ADVERTISE,
259 g_dhcp_client_register_event(dhcp_client,
260 G_DHCP_CLIENT_EVENT_REQUEST,
263 g_dhcp_client_register_event(dhcp_client,
264 G_DHCP_CLIENT_EVENT_CONFIRM,
267 g_dhcp_client_register_event(dhcp_client,
268 G_DHCP_CLIENT_EVENT_RENEW,
271 g_dhcp_client_register_event(dhcp_client,
272 G_DHCP_CLIENT_EVENT_REBIND,
275 g_dhcp_client_register_event(dhcp_client,
276 G_DHCP_CLIENT_EVENT_RELEASE,
279 g_dhcp_client_register_event(dhcp_client,
280 G_DHCP_CLIENT_EVENT_DECLINE,
283 g_dhcp_client_register_event(dhcp_client,
284 G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
288 static void info_req_cb(GDHCPClient *dhcp_client, gpointer user_data)
290 struct connman_dhcpv6 *dhcp = user_data;
291 struct connman_service *service;
293 GList *option, *list;
294 char **nameservers, **timeservers;
296 DBG("dhcpv6 information-request %p", dhcp);
298 service = connman_service_lookup_from_network(dhcp->network);
300 connman_error("Can not lookup service");
304 g_dhcpv6_client_clear_retransmit(dhcp_client);
306 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
307 entries = g_list_length(option);
309 nameservers = g_try_new0(char *, entries + 1);
311 for (i = 0, list = option; list; list = list->next, i++)
312 nameservers[i] = g_strdup(list->data);
315 if (!compare_string_arrays(nameservers, dhcp->nameservers)) {
316 if (dhcp->nameservers) {
317 for (i = 0; dhcp->nameservers[i]; i++)
318 __connman_service_nameserver_remove(service,
319 dhcp->nameservers[i],
321 g_strfreev(dhcp->nameservers);
324 dhcp->nameservers = nameservers;
326 for (i = 0; dhcp->nameservers &&
327 dhcp->nameservers[i]; i++)
328 __connman_service_nameserver_append(service,
329 dhcp->nameservers[i],
332 g_strfreev(nameservers);
335 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
336 entries = g_list_length(option);
338 timeservers = g_try_new0(char *, entries + 1);
340 for (i = 0, list = option; list; list = list->next, i++)
341 timeservers[i] = g_strdup(list->data);
344 if (!compare_string_arrays(timeservers, dhcp->timeservers)) {
345 if (dhcp->timeservers) {
346 for (i = 0; dhcp->timeservers[i]; i++)
347 __connman_service_timeserver_remove(service,
348 dhcp->timeservers[i]);
349 g_strfreev(dhcp->timeservers);
352 dhcp->timeservers = timeservers;
354 for (i = 0; dhcp->timeservers &&
355 dhcp->timeservers[i]; i++)
356 __connman_service_timeserver_append(service,
357 dhcp->timeservers[i]);
359 g_strfreev(timeservers);
362 if (dhcp->callback) {
363 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
364 dhcp->callback(dhcp->network, status == 0 ?
365 CONNMAN_DHCPV6_STATUS_SUCCEED :
366 CONNMAN_DHCPV6_STATUS_FAIL,
371 static int dhcpv6_info_request(struct connman_dhcpv6 *dhcp)
373 struct connman_service *service;
374 GDHCPClient *dhcp_client;
375 GDHCPClientError error;
378 DBG("dhcp %p", dhcp);
380 index = connman_network_get_index(dhcp->network);
382 dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
383 if (error != G_DHCP_CLIENT_ERROR_NONE) {
388 if (getenv("CONNMAN_DHCPV6_DEBUG"))
389 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
391 service = connman_service_lookup_from_network(dhcp->network);
394 g_dhcp_client_unref(dhcp_client);
398 ret = set_duid(service, dhcp->network, dhcp_client, index);
401 g_dhcp_client_unref(dhcp_client);
405 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
406 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
407 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
408 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
410 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
411 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
413 g_dhcp_client_register_event(dhcp_client,
414 G_DHCP_CLIENT_EVENT_INFORMATION_REQ, info_req_cb, dhcp);
416 dhcp->dhcp_client = dhcp_client;
418 return g_dhcp_client_start(dhcp_client, NULL);
421 static int check_ipv6_addr_prefix(GSList *prefixes, char *address)
423 struct in6_addr addr_prefix, addr;
427 for (list = prefixes; list; list = list->next) {
428 char *prefix = list->data;
429 const char *slash = g_strrstr(prefix, "/");
430 const unsigned char bits[] = { 0x00, 0xFE, 0xFC, 0xF8,
431 0xF0, 0xE0, 0xC0, 0x80 };
432 int left, count, i, plen;
437 prefix = g_strndup(prefix, slash - prefix);
438 len = strtol(slash + 1, NULL, 10);
439 if (len < 3 || len > 128)
448 inet_pton(AF_INET6, prefix, &addr_prefix);
449 inet_pton(AF_INET6, address, &addr);
451 memset(&addr_prefix.s6_addr[i], 0, count);
452 memset(&addr.s6_addr[i], 0, count);
455 addr_prefix.s6_addr[i - 1] &= bits[left];
456 addr.s6_addr[i - 1] &= bits[left];
461 if (memcmp(&addr_prefix, &addr, 16) == 0) {
470 static int set_other_addresses(GDHCPClient *dhcp_client,
471 struct connman_dhcpv6 *dhcp)
473 struct connman_service *service;
475 GList *option, *list;
476 char **nameservers, **timeservers;
478 service = connman_service_lookup_from_network(dhcp->network);
480 connman_error("Can not lookup service");
485 * Check domains before nameservers so that the nameserver append
486 * function will update domain list in service.c
488 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DOMAIN_LIST);
489 entries = g_list_length(option);
491 char **domains = g_try_new0(char *, entries + 1);
493 for (i = 0, list = option; list;
494 list = list->next, i++)
495 domains[i] = g_strdup(list->data);
496 __connman_service_update_search_domains(service, domains);
501 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
502 entries = g_list_length(option);
504 nameservers = g_try_new0(char *, entries + 1);
506 for (i = 0, list = option; list; list = list->next, i++)
507 nameservers[i] = g_strdup(list->data);
510 if (!compare_string_arrays(nameservers, dhcp->nameservers)) {
511 if (dhcp->nameservers) {
512 for (i = 0; dhcp->nameservers[i]; i++)
513 __connman_service_nameserver_remove(service,
514 dhcp->nameservers[i],
516 g_strfreev(dhcp->nameservers);
519 dhcp->nameservers = nameservers;
521 for (i = 0; dhcp->nameservers &&
522 dhcp->nameservers[i]; i++)
523 __connman_service_nameserver_append(service,
524 dhcp->nameservers[i],
527 g_strfreev(nameservers);
530 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
531 entries = g_list_length(option);
533 timeservers = g_try_new0(char *, entries + 1);
535 for (i = 0, list = option; list; list = list->next, i++)
536 timeservers[i] = g_strdup(list->data);
539 if (!compare_string_arrays(timeservers, dhcp->timeservers)) {
540 if (dhcp->timeservers) {
541 for (i = 0; dhcp->timeservers[i]; i++)
542 __connman_service_timeserver_remove(service,
543 dhcp->timeservers[i]);
544 g_strfreev(dhcp->timeservers);
547 dhcp->timeservers = timeservers;
549 for (i = 0; dhcp->timeservers &&
550 dhcp->timeservers[i]; i++)
551 __connman_service_timeserver_append(service,
552 dhcp->timeservers[i]);
554 g_strfreev(timeservers);
559 static GSList *copy_prefixes(GSList *prefixes)
561 GSList *list, *copy = NULL;
563 for (list = prefixes; list; list = list->next)
564 copy = g_slist_prepend(copy, g_strdup(list->data));
570 * Helper struct for doing DAD (duplicate address detection).
571 * It is refcounted and freed after all reply's to neighbor
572 * discovery request are received.
578 GDHCPClient *dhcp_client;
579 struct connman_ipconfig *ipconfig;
587 static void free_own_address(struct own_address *data)
589 g_dhcp_client_unref(data->dhcp_client);
590 __connman_ipconfig_unref(data->ipconfig);
591 g_slist_free_full(data->prefixes, free_prefix);
592 g_slist_free_full(data->dad_failed, g_free);
593 g_slist_free_full(data->dad_succeed, g_free);
598 static struct own_address *ref_own_address(struct own_address *address)
600 DBG("%p ref %d", address, address->refcount + 1);
602 __sync_fetch_and_add(&address->refcount, 1);
607 static void unref_own_address(struct own_address *address)
612 DBG("%p ref %d", address, address->refcount - 1);
614 if (__sync_fetch_and_sub(&address->refcount, 1) != 1)
617 free_own_address(address);
620 static void set_address(int ifindex, struct connman_ipconfig *ipconfig,
621 GSList *prefixes, char *address)
623 const char *c_address;
625 c_address = __connman_ipconfig_get_local(ipconfig);
627 if (address && ((c_address && g_strcmp0(address, c_address) != 0) ||
631 /* Is this prefix part of the subnet we are suppose to use? */
632 prefix_len = check_ipv6_addr_prefix(prefixes, address);
634 __connman_ipconfig_address_remove(ipconfig);
635 __connman_ipconfig_set_local(ipconfig, address);
636 __connman_ipconfig_set_prefixlen(ipconfig, prefix_len);
638 DBG("new address %s/%d", address, prefix_len);
640 __connman_ipconfig_set_dhcp_address(ipconfig, address);
641 __connman_service_save(
642 __connman_service_lookup_from_index(ifindex));
648 * Helper struct that is used when waiting a reply to DECLINE message.
650 struct decline_cb_data {
651 GDHCPClient *dhcp_client;
657 static void decline_reply_callback(struct decline_cb_data *data)
659 struct connman_network *network;
660 struct connman_service *service;
662 service = __connman_service_lookup_from_index(data->ifindex);
663 network = __connman_service_get_network(service);
666 data->callback(network, CONNMAN_DHCPV6_STATUS_RESTART, NULL);
668 g_dhcp_client_unref(data->dhcp_client);
671 static gboolean decline_timeout(gpointer user_data)
673 struct decline_cb_data *data = user_data;
675 DBG("ifindex %d", data->ifindex);
678 * We ignore all DECLINE replies that are received after the timeout
680 g_dhcp_client_register_event(data->dhcp_client,
681 G_DHCP_CLIENT_EVENT_DECLINE,
684 decline_reply_callback(data);
691 static void decline_cb(GDHCPClient *dhcp_client, gpointer user_data)
693 struct decline_cb_data *data = user_data;
695 DBG("ifindex %d", data->ifindex);
697 g_dhcpv6_client_clear_retransmit(dhcp_client);
700 g_source_remove(data->timeout);
702 decline_reply_callback(data);
707 static int dhcpv6_decline(GDHCPClient *dhcp_client, int ifindex,
708 dhcpv6_cb callback, GSList *failed)
710 struct decline_cb_data *data;
714 DBG("dhcp_client %p", dhcp_client);
716 g_dhcp_client_clear_requests(dhcp_client);
718 g_dhcpv6_client_clear_send(dhcp_client, G_DHCPV6_ORO);
720 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
721 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
723 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
725 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
727 code = G_DHCPV6_IA_TA;
731 code = G_DHCPV6_IA_NA;
733 g_dhcpv6_client_clear_send(dhcp_client, code);
735 g_dhcpv6_client_set_ias(dhcp_client, ifindex, code, NULL, NULL,
738 clear_callbacks(dhcp_client);
740 data = g_try_new(struct decline_cb_data, 1);
744 data->ifindex = ifindex;
745 data->callback = callback;
746 data->dhcp_client = g_dhcp_client_ref(dhcp_client);
747 data->timeout = g_timeout_add(DEC_TIMEOUT, decline_timeout, data);
749 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_DECLINE,
752 return g_dhcp_client_start(dhcp_client, NULL);
755 static void dad_reply(struct nd_neighbor_advert *reply,
756 unsigned int length, struct in6_addr *addr, void *user_data)
758 struct own_address *data = user_data;
760 char address[INET6_ADDRSTRLEN];
761 enum __connman_dhcpv6_status status = CONNMAN_DHCPV6_STATUS_FAIL;
763 inet_ntop(AF_INET6, addr, address, INET6_ADDRSTRLEN);
765 DBG("user %p reply %p len %d address %s index %d data %p", user_data,
766 reply, length, address, data->ifindex, data);
770 DBG("DAD succeed for %s", address);
772 DBG("DAD cannot be done for %s", address);
774 data->dad_succeed = g_slist_prepend(data->dad_succeed,
778 DBG("DAD failed for %s", address);
780 data->dad_failed = g_slist_prepend(data->dad_failed,
785 * If refcount == 1 then we got the final reply and can continue.
787 if (data->refcount > 1)
790 for (list = data->dad_succeed; list; list = list->next)
791 set_address(data->ifindex, data->ipconfig, data->prefixes,
794 if (data->dad_failed) {
795 dhcpv6_decline(data->dhcp_client, data->ifindex,
796 data->callback, data->dad_failed);
798 if (data->dad_succeed)
799 status = CONNMAN_DHCPV6_STATUS_SUCCEED;
801 if (data->callback) {
802 struct connman_network *network;
803 struct connman_service *service;
805 service = __connman_service_lookup_from_index(
807 network = __connman_service_get_network(service);
809 data->callback(network, status, NULL);
813 unref_own_address(data);
817 * Is the kernel configured to do DAD? If 0, then do not do DAD.
818 * See also RFC 4862 chapter 5.4 about DupAddrDetectTransmits
820 static int dad_transmits(int ifindex)
822 char name[IF_NAMESIZE];
827 if (!if_indextoname(ifindex, name))
830 path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/dad_transmits",
836 f = fopen(path, "r");
841 if (fscanf(f, "%d", &value) < 0)
850 static void do_dad(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
852 struct connman_service *service;
853 struct connman_ipconfig *ipconfig;
855 GList *option, *list;
856 struct own_address *user_data;
858 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
860 option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
863 * Even if we didn't had any addresses, just try to set
866 set_other_addresses(dhcp_client, dhcp);
869 DBG("Skip DAD as no addresses found in reply");
871 dhcp->callback(dhcp->network,
872 CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
877 ifindex = connman_network_get_index(dhcp->network);
879 DBG("index %d", ifindex);
881 service = connman_service_lookup_from_network(dhcp->network);
883 connman_error("Can not lookup service for index %d", ifindex);
887 ipconfig = __connman_service_get_ip6config(service);
889 connman_error("Could not lookup ip6config for index %d",
894 if (!dad_transmits(ifindex)) {
895 DBG("Skip DAD because of kernel configuration");
897 for (list = option; list; list = list->next)
898 set_address(ifindex, ipconfig, dhcp->prefixes,
902 dhcp->callback(dhcp->network,
903 CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
908 if (g_list_length(option) == 0) {
909 DBG("No addresses when doing DAD");
911 dhcp->callback(dhcp->network,
912 CONNMAN_DHCPV6_STATUS_SUCCEED, NULL);
917 user_data = g_try_new0(struct own_address, 1);
921 user_data->refcount = 0;
922 user_data->ifindex = ifindex;
923 user_data->dhcp_client = g_dhcp_client_ref(dhcp_client);
924 user_data->ipconfig = __connman_ipconfig_ref(ipconfig);
925 user_data->prefixes = copy_prefixes(dhcp->prefixes);
926 user_data->callback = dhcp->callback;
929 * We send one neighbor discovery request / address
930 * and after all checks are done, then report the status
934 for (list = option; list; list = list->next) {
935 char *address = option->data;
936 struct in6_addr addr;
939 ref_own_address(user_data);
941 if (inet_pton(AF_INET6, address, &addr) < 0) {
942 DBG("Invalid IPv6 address %s %d/%s", address,
943 -errno, strerror(errno));
947 DBG("user %p address %s client %p ipconfig %p prefixes %p",
949 user_data->dhcp_client, user_data->ipconfig,
950 user_data->prefixes);
952 ret = __connman_inet_ipv6_do_dad(ifindex, 1000,
957 DBG("Could not send neighbor solicitation for %s",
959 dad_reply(NULL, -1, &addr, user_data);
961 DBG("Sent neighbor solicitation %d bytes", ret);
968 unref_own_address(user_data);
972 dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
976 static gboolean timeout_request_resend(gpointer user_data)
978 struct connman_dhcpv6 *dhcp = user_data;
980 if (dhcp->request_count >= REQ_MAX_RC) {
981 DBG("max request retry attempts %d", dhcp->request_count);
982 dhcp->request_count = 0;
984 dhcp->callback(dhcp->network,
985 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
989 dhcp->request_count++;
991 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
992 DBG("request resend RT timeout %d msec", dhcp->RT);
993 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request_resend, dhcp);
995 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
997 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1002 static gboolean request_resend(gpointer user_data)
1004 struct connman_dhcpv6 *dhcp = user_data;
1006 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1008 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1009 DBG("request resend RT timeout %d msec", dhcp->RT);
1010 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request_resend, dhcp);
1012 dhcpv6_request(dhcp, true);
1017 static void do_resend_request(struct connman_dhcpv6 *dhcp)
1019 if (dhcp->request_count >= REQ_MAX_RC) {
1020 DBG("max request retry attempts %d", dhcp->request_count);
1021 dhcp->request_count = 0;
1023 dhcp->callback(dhcp->network,
1024 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1028 dhcp->request_count++;
1030 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1031 DBG("resending request after %d msec", dhcp->RT);
1032 dhcp->timeout = g_timeout_add(dhcp->RT, request_resend, dhcp);
1035 static void re_cb(enum request_type req_type, GDHCPClient *dhcp_client,
1038 struct connman_dhcpv6 *dhcp = user_data;
1043 status = g_dhcpv6_client_get_status(dhcp_client);
1045 DBG("dhcpv6 cb msg %p status %d", dhcp, status);
1048 * RFC 3315, 18.1.8 handle the resend if error
1050 if (status == G_DHCPV6_ERROR_BINDING) {
1051 dhcpv6_request(dhcp, false);
1052 } else if (status == G_DHCPV6_ERROR_MCAST) {
1055 dhcpv6_request(dhcp, true);
1058 dhcpv6_rebind(dhcp);
1064 } else if (status == G_DHCPV6_ERROR_LINK) {
1065 if (req_type == REQ_REQUEST) {
1066 g_dhcp_client_unref(dhcp->dhcp_client);
1067 start_solicitation(dhcp);
1070 dhcp->callback(dhcp->network,
1071 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1073 } else if (status == G_DHCPV6_ERROR_FAILURE) {
1074 if (req_type == REQ_REQUEST) {
1075 /* Rate limit the resend of request message */
1076 do_resend_request(dhcp);
1079 dhcp->callback(dhcp->network,
1080 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1085 * If we did not got any addresses, then re-send
1090 option = g_dhcp_client_get_option(dhcp->dhcp_client,
1093 option = g_dhcp_client_get_option(dhcp->dhcp_client,
1098 dhcpv6_request(dhcp, true);
1101 dhcpv6_rebind(dhcp);
1111 if (status == G_DHCPV6_ERROR_SUCCESS)
1112 do_dad(dhcp_client, dhcp);
1113 else if (dhcp->callback)
1114 dhcp->callback(dhcp->network,
1115 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1119 static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data)
1123 g_dhcpv6_client_reset_request(dhcp_client);
1124 g_dhcpv6_client_clear_retransmit(dhcp_client);
1126 re_cb(REQ_REBIND, dhcp_client, user_data);
1129 static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp)
1131 GDHCPClient *dhcp_client;
1133 DBG("dhcp %p", dhcp);
1135 dhcp_client = dhcp->dhcp_client;
1137 g_dhcp_client_clear_requests(dhcp_client);
1139 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1140 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1141 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1142 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1144 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1145 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1147 g_dhcpv6_client_set_ia(dhcp_client,
1148 connman_network_get_index(dhcp->network),
1149 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1150 NULL, NULL, TRUE, NULL);
1152 clear_callbacks(dhcp_client);
1154 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
1157 dhcp->dhcp_client = dhcp_client;
1159 return g_dhcp_client_start(dhcp_client, NULL);
1162 static gboolean dhcpv6_restart(gpointer user_data)
1164 struct connman_dhcpv6 *dhcp = user_data;
1167 dhcp->callback(dhcp->network, CONNMAN_DHCPV6_STATUS_FAIL,
1174 * Check if we need to restart the solicitation procedure. This
1175 * is done if all the addresses have expired. RFC 3315, 18.1.4
1177 static int check_restart(struct connman_dhcpv6 *dhcp)
1179 time_t current, expired;
1181 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
1183 current = time(NULL);
1185 if (current >= expired) {
1186 DBG("expired by %d secs", (int)(current - expired));
1188 g_timeout_add(0, dhcpv6_restart, dhcp);
1196 static gboolean timeout_rebind(gpointer user_data)
1198 struct connman_dhcpv6 *dhcp = user_data;
1200 if (check_restart(dhcp) < 0)
1203 dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
1205 DBG("rebind RT timeout %d msec", dhcp->RT);
1207 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1209 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1211 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1216 static gboolean start_rebind(gpointer user_data)
1218 struct connman_dhcpv6 *dhcp = user_data;
1220 if (check_restart(dhcp) < 0)
1223 dhcp->RT = REB_TIMEOUT * (1 + get_random());
1225 DBG("rebind initial RT timeout %d msec", dhcp->RT);
1227 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
1229 dhcpv6_rebind(dhcp);
1234 static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
1238 g_dhcpv6_client_reset_request(dhcp_client);
1239 g_dhcpv6_client_clear_retransmit(dhcp_client);
1241 re_cb(REQ_REQUEST, dhcp_client, user_data);
1244 static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
1247 GDHCPClient *dhcp_client;
1250 DBG("dhcp %p add %d", dhcp, add_addresses);
1252 dhcp_client = dhcp->dhcp_client;
1254 g_dhcp_client_clear_requests(dhcp_client);
1256 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1257 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1258 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1259 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1260 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1262 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1263 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1265 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1266 g_dhcpv6_client_set_ia(dhcp_client,
1267 connman_network_get_index(dhcp->network),
1268 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1269 &T1, &T2, add_addresses, NULL);
1271 clear_callbacks(dhcp_client);
1273 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
1276 dhcp->dhcp_client = dhcp_client;
1278 return g_dhcp_client_start(dhcp_client, NULL);
1281 static gboolean timeout_request(gpointer user_data)
1283 struct connman_dhcpv6 *dhcp = user_data;
1285 if (dhcp->request_count >= REQ_MAX_RC) {
1286 DBG("max request retry attempts %d", dhcp->request_count);
1287 dhcp->request_count = 0;
1289 dhcp->callback(dhcp->network,
1290 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1294 dhcp->request_count++;
1296 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
1297 DBG("request RT timeout %d msec", dhcp->RT);
1298 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1300 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1302 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1307 static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
1311 g_dhcpv6_client_reset_request(dhcp_client);
1312 g_dhcpv6_client_clear_retransmit(dhcp_client);
1314 re_cb(REQ_RENEW, dhcp_client, user_data);
1317 static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
1319 GDHCPClient *dhcp_client;
1322 DBG("dhcp %p", dhcp);
1324 dhcp_client = dhcp->dhcp_client;
1326 g_dhcp_client_clear_requests(dhcp_client);
1328 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1329 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1330 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1331 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1332 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1334 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1335 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1337 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
1338 g_dhcpv6_client_set_ia(dhcp_client,
1339 connman_network_get_index(dhcp->network),
1340 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1341 &T1, &T2, TRUE, NULL);
1343 clear_callbacks(dhcp_client);
1345 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
1348 dhcp->dhcp_client = dhcp_client;
1350 return g_dhcp_client_start(dhcp_client, NULL);
1353 static gboolean timeout_renew(gpointer user_data)
1355 struct connman_dhcpv6 *dhcp = user_data;
1356 time_t last_rebind, current;
1359 if (check_restart(dhcp) < 0)
1362 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, &T2,
1363 &last_rebind, NULL);
1364 current = time(NULL);
1365 if ((unsigned)current >= (unsigned)last_rebind + T2) {
1367 * Do rebind instead if past T2
1373 dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
1375 DBG("renew RT timeout %d msec", dhcp->RT);
1377 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1379 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1381 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1386 static gboolean start_renew(gpointer user_data)
1388 struct connman_dhcpv6 *dhcp = user_data;
1390 dhcp->RT = REN_TIMEOUT * (1 + get_random());
1392 DBG("renew initial RT timeout %d msec", dhcp->RT);
1394 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
1401 int __connman_dhcpv6_start_renew(struct connman_network *network,
1404 struct connman_dhcpv6 *dhcp;
1405 uint32_t T1, T2, delta;
1406 time_t started, current, expired;
1408 dhcp = g_hash_table_lookup(network_table, network);
1412 DBG("network %p dhcp %p", network, dhcp);
1416 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
1417 &started, &expired);
1419 current = time(NULL);
1421 DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
1422 (unsigned long)expired, current, started);
1424 if (T1 == 0xffffffff)
1425 /* RFC 3315, 22.4 */
1430 * Client can choose the timeout.
1432 T1 = (expired - started) / 2;
1433 T2 = (expired - started) / 10 * 8;
1436 dhcp->callback = callback;
1438 /* RFC 3315, 18.1.4, start solicit if expired */
1439 if (check_restart(dhcp) < 0)
1442 if (T2 != 0xffffffff && T2 > 0) {
1443 if ((unsigned)current >= (unsigned)started + T2) {
1444 /* RFC 3315, chapter 18.1.3, start rebind */
1445 DBG("start rebind immediately");
1447 dhcp->timeout = g_timeout_add_seconds(0, start_rebind,
1450 } else if ((unsigned)current < (unsigned)started + T1) {
1451 delta = started + T1 - current;
1452 DBG("renew after %d secs", delta);
1454 dhcp->timeout = g_timeout_add_seconds(delta,
1457 delta = started + T2 - current;
1458 DBG("rebind after %d secs", delta);
1460 dhcp->timeout = g_timeout_add_seconds(delta,
1461 start_rebind, dhcp);
1468 static void release_cb(GDHCPClient *dhcp_client, gpointer user_data)
1473 int __connman_dhcpv6_start_release(struct connman_network *network,
1476 struct connman_dhcpv6 *dhcp;
1477 GDHCPClient *dhcp_client;
1480 return 0; /* we are already released */
1482 dhcp = g_hash_table_lookup(network_table, network);
1486 DBG("network %p dhcp %p client %p stateless %d", network, dhcp,
1487 dhcp->dhcp_client, dhcp->stateless);
1489 if (dhcp->stateless)
1494 dhcp_client = dhcp->dhcp_client;
1497 * We had started the DHCPv6 handshaking i.e., we have called
1498 * __connman_dhcpv6_start() but it has not yet sent
1499 * a solicitation message to server. This means that we do not
1500 * have DHCPv6 configured yet so we can just quit here.
1502 DBG("DHCPv6 was not started");
1506 g_dhcp_client_clear_requests(dhcp_client);
1507 g_dhcp_client_clear_values(dhcp_client);
1509 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1510 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
1512 g_dhcpv6_client_set_ia(dhcp_client,
1513 connman_network_get_index(dhcp->network),
1514 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1515 NULL, NULL, TRUE, NULL);
1517 clear_callbacks(dhcp_client);
1520 * Although we register a callback here we are really not interested in
1521 * the answer because it might take too long time and network code
1522 * might be in the middle of the disconnect.
1523 * So we just inform the server that we are done with the addresses
1524 * but ignore the reply from server. This is allowed by RFC 3315
1527 g_dhcp_client_register_event(dhcp_client,
1528 G_DHCP_CLIENT_EVENT_RELEASE,
1531 dhcp->dhcp_client = dhcp_client;
1533 return g_dhcp_client_start(dhcp_client, NULL);
1536 static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
1538 DBG("dhcp %p", dhcp);
1544 if (!dhcp->dhcp_client)
1547 g_dhcp_client_stop(dhcp->dhcp_client);
1548 g_dhcp_client_unref(dhcp->dhcp_client);
1550 dhcp->dhcp_client = NULL;
1555 static void remove_network(gpointer user_data)
1557 struct connman_dhcpv6 *dhcp = user_data;
1559 DBG("dhcp %p", dhcp);
1561 dhcpv6_release(dhcp);
1566 static gboolean timeout_info_req(gpointer user_data)
1568 struct connman_dhcpv6 *dhcp = user_data;
1570 dhcp->RT = calc_delay(dhcp->RT, INF_MAX_RT);
1572 DBG("info RT timeout %d msec", dhcp->RT);
1574 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1576 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1578 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1583 static gboolean start_info_req(gpointer user_data)
1585 struct connman_dhcpv6 *dhcp = user_data;
1587 /* Set the retransmission timeout, RFC 3315 chapter 14 */
1588 dhcp->RT = INF_TIMEOUT * (1 + get_random());
1590 DBG("info initial RT timeout %d msec", dhcp->RT);
1592 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
1594 dhcpv6_info_request(dhcp);
1599 int __connman_dhcpv6_start_info(struct connman_network *network,
1602 struct connman_dhcpv6 *dhcp;
1607 if (network_table) {
1608 dhcp = g_hash_table_lookup(network_table, network);
1609 if (dhcp && dhcp->started)
1613 dhcp = g_try_new0(struct connman_dhcpv6, 1);
1617 dhcp->network = network;
1618 dhcp->callback = callback;
1619 dhcp->stateless = true;
1620 dhcp->started = true;
1622 connman_network_ref(network);
1624 DBG("replace network %p dhcp %p", network, dhcp);
1626 g_hash_table_replace(network_table, network, dhcp);
1628 /* Initial timeout, RFC 3315, 18.1.5 */
1629 delay = rand() % 1000;
1631 dhcp->timeout = g_timeout_add(delay, start_info_req, dhcp);
1636 static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data)
1638 struct connman_dhcpv6 *dhcp = user_data;
1640 DBG("dhcpv6 advertise msg %p", dhcp);
1644 g_dhcpv6_client_clear_retransmit(dhcp_client);
1646 if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
1648 dhcp->callback(dhcp->network,
1649 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1653 dhcp->RT = REQ_TIMEOUT * (1 + get_random());
1654 DBG("request initial RT timeout %d msec", dhcp->RT);
1655 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
1657 dhcp->request_count = 1;
1659 dhcpv6_request(dhcp, true);
1662 static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)
1664 /* We get here if server supports rapid commit */
1665 struct connman_dhcpv6 *dhcp = user_data;
1667 DBG("dhcpv6 solicitation msg %p", dhcp);
1671 do_dad(dhcp_client, dhcp);
1673 g_dhcpv6_client_clear_retransmit(dhcp_client);
1676 static gboolean timeout_solicitation(gpointer user_data)
1678 struct connman_dhcpv6 *dhcp = user_data;
1680 dhcp->RT = calc_delay(dhcp->RT, SOL_MAX_RT);
1682 DBG("solicit RT timeout %d msec", dhcp->RT);
1684 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1686 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
1688 g_dhcp_client_start(dhcp->dhcp_client, NULL);
1693 static int dhcpv6_solicitation(struct connman_dhcpv6 *dhcp)
1695 struct connman_service *service;
1696 struct connman_ipconfig *ipconfig_ipv6;
1697 GDHCPClient *dhcp_client;
1698 GDHCPClientError error;
1701 DBG("dhcp %p", dhcp);
1703 index = connman_network_get_index(dhcp->network);
1705 dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
1706 if (error != G_DHCP_CLIENT_ERROR_NONE) {
1711 if (getenv("CONNMAN_DHCPV6_DEBUG"))
1712 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
1714 service = connman_service_lookup_from_network(dhcp->network);
1717 g_dhcp_client_unref(dhcp_client);
1721 ret = set_duid(service, dhcp->network, dhcp_client, index);
1724 g_dhcp_client_unref(dhcp_client);
1728 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
1729 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
1730 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
1731 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DOMAIN_LIST);
1732 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
1734 g_dhcpv6_client_set_oro(dhcp_client, 3, G_DHCPV6_DNS_SERVERS,
1735 G_DHCPV6_DOMAIN_LIST, G_DHCPV6_SNTP_SERVERS);
1737 ipconfig_ipv6 = __connman_service_get_ip6config(service);
1738 dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
1740 g_dhcpv6_client_set_ia(dhcp_client, index,
1741 dhcp->use_ta ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
1742 NULL, NULL, FALSE, NULL);
1744 clear_callbacks(dhcp_client);
1746 g_dhcp_client_register_event(dhcp_client,
1747 G_DHCP_CLIENT_EVENT_SOLICITATION,
1748 solicitation_cb, dhcp);
1750 g_dhcp_client_register_event(dhcp_client,
1751 G_DHCP_CLIENT_EVENT_ADVERTISE,
1752 advertise_cb, dhcp);
1754 dhcp->dhcp_client = dhcp_client;
1756 return g_dhcp_client_start(dhcp_client, NULL);
1759 static gboolean start_solicitation(gpointer user_data)
1761 struct connman_dhcpv6 *dhcp = user_data;
1763 /* Set the retransmission timeout, RFC 3315 chapter 14 */
1764 dhcp->RT = SOL_TIMEOUT * (1 + get_random());
1766 DBG("solicit initial RT timeout %d msec", dhcp->RT);
1768 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
1770 dhcpv6_solicitation(dhcp);
1775 int __connman_dhcpv6_start(struct connman_network *network,
1776 GSList *prefixes, dhcpv6_cb callback)
1778 struct connman_service *service;
1779 struct connman_dhcpv6 *dhcp;
1784 if (network_table) {
1785 dhcp = g_hash_table_lookup(network_table, network);
1786 if (dhcp && dhcp->started)
1790 service = connman_service_lookup_from_network(network);
1794 dhcp = g_try_new0(struct connman_dhcpv6, 1);
1798 dhcp->network = network;
1799 dhcp->callback = callback;
1800 dhcp->prefixes = prefixes;
1801 dhcp->started = true;
1803 connman_network_ref(network);
1805 DBG("replace network %p dhcp %p", network, dhcp);
1807 g_hash_table_replace(network_table, network, dhcp);
1809 /* Initial timeout, RFC 3315, 17.1.2 */
1810 delay = rand() % 1000;
1813 * Start from scratch.
1814 * RFC 3315, chapter 17.1.2 Solicitation message
1816 * Note that we do not send CONFIRM message here as it does
1817 * not make much sense because we do not save expiration time
1818 * so we cannot really know how long the saved address is valid
1819 * anyway. The reply to CONFIRM message does not send
1820 * expiration times back to us. Because of this we need to
1821 * start using SOLICITATION anyway.
1823 dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
1828 void __connman_dhcpv6_stop(struct connman_network *network)
1835 if (g_hash_table_remove(network_table, network))
1836 connman_network_unref(network);
1839 static int save_prefixes(struct connman_ipconfig *ipconfig,
1843 int i = 0, count = g_slist_length(prefixes);
1849 array = g_try_new0(char *, count + 1);
1853 for (list = prefixes; list; list = list->next) {
1854 char *elem, addr_str[INET6_ADDRSTRLEN];
1855 GDHCPIAPrefix *prefix = list->data;
1857 elem = g_strdup_printf("%s/%d", inet_ntop(AF_INET6,
1858 &prefix->prefix, addr_str, INET6_ADDRSTRLEN),
1868 __connman_ipconfig_set_dhcpv6_prefixes(ipconfig, array);
1872 static GSList *load_prefixes(struct connman_ipconfig *ipconfig)
1875 GSList *list = NULL;
1876 char **array = __connman_ipconfig_get_dhcpv6_prefixes(ipconfig);
1881 for (i = 0; array[i]; i++) {
1882 GDHCPIAPrefix *prefix;
1884 char *ptr, **elems = g_strsplit(array[i], "/", 0);
1889 value = strtol(elems[1], &ptr, 10);
1890 if (ptr != elems[1] && *ptr == '\0' && value <= 128) {
1891 struct in6_addr addr;
1893 if (inet_pton(AF_INET6, elems[0], &addr) == 1) {
1894 prefix = g_try_new0(GDHCPIAPrefix, 1);
1899 memcpy(&prefix->prefix, &addr,
1900 sizeof(struct in6_addr));
1901 prefix->prefixlen = value;
1903 list = g_slist_prepend(list, prefix);
1913 static GDHCPIAPrefix *copy_prefix(gpointer data)
1915 GDHCPIAPrefix *copy, *prefix = data;
1917 copy = g_try_new(GDHCPIAPrefix, 1);
1921 memcpy(copy, prefix, sizeof(GDHCPIAPrefix));
1926 static GSList *copy_and_convert_prefixes(GList *prefixes)
1928 GSList *copy = NULL;
1931 for (list = prefixes; list; list = list->next)
1932 copy = g_slist_prepend(copy, copy_prefix(list->data));
1937 static int set_prefixes(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
1940 g_slist_free_full(dhcp->prefixes, free_prefix);
1943 copy_and_convert_prefixes(g_dhcp_client_get_option(dhcp_client,
1946 DBG("Got %d prefix", g_slist_length(dhcp->prefixes));
1948 if (dhcp->callback) {
1949 uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
1950 if (status == G_DHCPV6_ERROR_NO_PREFIX)
1951 dhcp->callback(dhcp->network,
1952 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
1954 struct connman_service *service;
1955 struct connman_ipconfig *ipconfig;
1956 int ifindex = connman_network_get_index(dhcp->network);
1958 service = __connman_service_lookup_from_index(ifindex);
1960 ipconfig = __connman_service_get_ip6config(
1962 save_prefixes(ipconfig, dhcp->prefixes);
1963 __connman_service_save(service);
1966 dhcp->callback(dhcp->network,
1967 CONNMAN_DHCPV6_STATUS_SUCCEED, dhcp->prefixes);
1970 g_slist_free_full(dhcp->prefixes, free_prefix);
1971 dhcp->prefixes = NULL;
1977 static void re_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
1979 struct connman_dhcpv6 *dhcp = user_data;
1983 status = g_dhcpv6_client_get_status(dhcp_client);
1985 DBG("dhcpv6 cb msg %p status %d", dhcp, status);
1987 if (status == G_DHCPV6_ERROR_BINDING) {
1988 /* RFC 3315, 18.1.8 */
1989 dhcpv6_pd_request(dhcp);
1991 ret = set_prefixes(dhcp_client, dhcp);
1994 dhcp->callback(dhcp->network,
1995 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2001 static void rebind_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2005 g_dhcpv6_client_clear_retransmit(dhcp_client);
2007 re_pd_cb(dhcp_client, user_data);
2010 static GDHCPClient *create_pd_client(struct connman_dhcpv6 *dhcp, int *err)
2012 GDHCPClient *dhcp_client;
2013 GDHCPClientError error;
2014 struct connman_service *service;
2018 index = connman_network_get_index(dhcp->network);
2020 dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
2021 if (error != G_DHCP_CLIENT_ERROR_NONE) {
2026 if (getenv("CONNMAN_DHCPV6_DEBUG"))
2027 g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6:PD");
2029 service = connman_service_lookup_from_network(dhcp->network);
2031 g_dhcp_client_unref(dhcp_client);
2036 ret = set_duid(service, dhcp->network, dhcp_client, index);
2038 g_dhcp_client_unref(dhcp_client);
2043 g_dhcpv6_client_create_iaid(dhcp_client, index, (unsigned char *)&iaid);
2044 g_dhcpv6_client_set_iaid(dhcp_client, iaid);
2049 static int dhcpv6_pd_rebind(struct connman_dhcpv6 *dhcp)
2051 GDHCPClient *dhcp_client;
2054 DBG("dhcp %p", dhcp);
2056 if (!dhcp->dhcp_client) {
2058 * We skipped the solicitation phase
2062 dhcp->dhcp_client = create_pd_client(dhcp, &err);
2063 if (!dhcp->dhcp_client) {
2069 dhcp_client = dhcp->dhcp_client;
2071 g_dhcp_client_clear_requests(dhcp_client);
2073 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2074 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2075 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2077 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2078 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2080 clear_callbacks(dhcp_client);
2082 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
2083 rebind_pd_cb, dhcp);
2085 return g_dhcp_client_start(dhcp_client, NULL);
2088 static void renew_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2092 g_dhcpv6_client_clear_retransmit(dhcp_client);
2094 re_pd_cb(dhcp_client, user_data);
2097 static int dhcpv6_pd_renew(struct connman_dhcpv6 *dhcp)
2099 GDHCPClient *dhcp_client;
2102 DBG("dhcp %p", dhcp);
2104 dhcp_client = dhcp->dhcp_client;
2106 g_dhcp_client_clear_requests(dhcp_client);
2108 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2109 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2110 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2111 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2113 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2114 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2116 clear_callbacks(dhcp_client);
2118 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
2121 return g_dhcp_client_start(dhcp_client, NULL);
2125 * Check if we need to restart the solicitation procedure. This
2126 * is done if all the prefixes have expired.
2128 static int check_pd_restart(struct connman_dhcpv6 *dhcp)
2130 time_t current, expired;
2132 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
2134 current = time(NULL);
2136 if (current > expired) {
2137 DBG("expired by %d secs", (int)(current - expired));
2139 g_timeout_add(0, dhcpv6_restart, dhcp);
2147 static gboolean timeout_pd_rebind(gpointer user_data)
2149 struct connman_dhcpv6 *dhcp = user_data;
2151 if (check_pd_restart(dhcp) < 0)
2154 dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
2156 DBG("rebind RT timeout %d msec", dhcp->RT);
2158 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2160 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2162 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2167 static gboolean start_pd_rebind(gpointer user_data)
2169 struct connman_dhcpv6 *dhcp = user_data;
2171 if (check_pd_restart(dhcp) < 0)
2174 dhcp->RT = REB_TIMEOUT * (1 + get_random());
2176 DBG("rebind initial RT timeout %d msec", dhcp->RT);
2178 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_rebind, dhcp);
2180 dhcpv6_pd_rebind(dhcp);
2185 static gboolean timeout_pd_rebind_confirm(gpointer user_data)
2187 struct connman_dhcpv6 *dhcp = user_data;
2189 dhcp->RT = calc_delay(dhcp->RT, CNF_MAX_RT);
2191 DBG("rebind with confirm RT timeout %d msec", dhcp->RT);
2193 dhcp->timeout = g_timeout_add(dhcp->RT,
2194 timeout_pd_rebind_confirm, dhcp);
2196 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2198 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2203 static gboolean timeout_pd_max_confirm(gpointer user_data)
2205 struct connman_dhcpv6 *dhcp = user_data;
2211 DBG("rebind with confirm max retransmit duration timeout");
2213 g_dhcpv6_client_clear_retransmit(dhcp->dhcp_client);
2216 dhcp->callback(dhcp->network,
2217 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2222 static gboolean start_pd_rebind_with_confirm(gpointer user_data)
2224 struct connman_dhcpv6 *dhcp = user_data;
2226 dhcp->RT = CNF_TIMEOUT * (1 + get_random());
2228 DBG("rebind with confirm initial RT timeout %d msec", dhcp->RT);
2230 dhcp->timeout = g_timeout_add(dhcp->RT,
2231 timeout_pd_rebind_confirm, dhcp);
2232 dhcp->MRD = g_timeout_add(CNF_MAX_RD, timeout_pd_max_confirm, dhcp);
2234 dhcpv6_pd_rebind(dhcp);
2239 static gboolean timeout_pd_renew(gpointer user_data)
2241 struct connman_dhcpv6 *dhcp = user_data;
2243 if (check_pd_restart(dhcp) < 0)
2246 dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
2248 DBG("renew RT timeout %d msec", dhcp->RT);
2250 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
2252 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2254 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2259 static gboolean start_pd_renew(gpointer user_data)
2261 struct connman_dhcpv6 *dhcp = user_data;
2263 dhcp->RT = REN_TIMEOUT * (1 + get_random());
2265 DBG("renew initial RT timeout %d msec", dhcp->RT);
2267 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_renew, dhcp);
2269 dhcpv6_pd_renew(dhcp);
2274 int __connman_dhcpv6_start_pd_renew(struct connman_network *network,
2277 struct connman_dhcpv6 *dhcp;
2279 time_t started, current, expired;
2281 dhcp = g_hash_table_lookup(network_pd_table, network);
2285 DBG("network %p dhcp %p", network, dhcp);
2289 g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
2290 &started, &expired);
2292 current = time(NULL);
2294 DBG("T1 %u T2 %u expires %lu current %lu started %lu", T1, T2,
2295 expired, current, started);
2297 if (T1 == 0xffffffff)
2298 /* RFC 3633, ch 9 */
2303 * Client can choose the timeout.
2307 dhcp->callback = callback;
2309 /* RFC 3315, 18.1.4, start solicit if expired */
2310 if (check_pd_restart(dhcp) < 0)
2313 if (T2 != 0xffffffff && T2 > 0) {
2314 if ((unsigned)current >= (unsigned)started + T2) {
2315 /* RFC 3315, chapter 18.1.3, start rebind */
2316 DBG("rebind after %d secs", T2);
2318 dhcp->timeout = g_timeout_add_seconds(T2,
2322 } else if ((unsigned)current < (unsigned)started + T1) {
2323 DBG("renew after %d secs", T1);
2325 dhcp->timeout = g_timeout_add_seconds(T1,
2329 DBG("rebind after %d secs", T2 - T1);
2331 dhcp->timeout = g_timeout_add_seconds(T2 - T1,
2340 static void release_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2345 int __connman_dhcpv6_start_pd_release(struct connman_network *network,
2348 struct connman_dhcpv6 *dhcp;
2349 GDHCPClient *dhcp_client;
2353 return 0; /* we are already released */
2355 dhcp = g_hash_table_lookup(network_pd_table, network);
2359 DBG("network %p dhcp %p client %p", network, dhcp, dhcp->dhcp_client);
2363 dhcp_client = dhcp->dhcp_client;
2365 DBG("DHCPv6 PD was not started");
2369 g_dhcp_client_clear_requests(dhcp_client);
2370 g_dhcp_client_clear_values(dhcp_client);
2372 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2373 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2375 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2376 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2378 clear_callbacks(dhcp_client);
2380 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RELEASE,
2381 release_pd_cb, dhcp);
2383 dhcp->dhcp_client = dhcp_client;
2385 return g_dhcp_client_start(dhcp_client, NULL);
2388 static gboolean timeout_pd_request(gpointer user_data)
2390 struct connman_dhcpv6 *dhcp = user_data;
2392 if (dhcp->request_count >= REQ_MAX_RC) {
2393 DBG("max request retry attempts %d", dhcp->request_count);
2394 dhcp->request_count = 0;
2396 dhcp->callback(dhcp->network,
2397 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2401 dhcp->request_count++;
2403 dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
2404 DBG("request RT timeout %d msec", dhcp->RT);
2405 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2407 g_dhcpv6_client_set_retransmit(dhcp->dhcp_client);
2409 g_dhcp_client_start(dhcp->dhcp_client, NULL);
2414 static void request_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2416 struct connman_dhcpv6 *dhcp = user_data;
2421 g_dhcpv6_client_clear_retransmit(dhcp_client);
2423 status = g_dhcpv6_client_get_status(dhcp_client);
2425 DBG("dhcpv6 pd cb msg %p status %d", dhcp, status);
2427 if (status == G_DHCPV6_ERROR_BINDING) {
2428 /* RFC 3315, 18.1.8 */
2429 dhcpv6_pd_request(dhcp);
2431 set_prefixes(dhcp_client, dhcp);
2435 static int dhcpv6_pd_request(struct connman_dhcpv6 *dhcp)
2437 GDHCPClient *dhcp_client;
2438 uint32_t T1 = 0, T2 = 0;
2440 DBG("dhcp %p", dhcp);
2442 dhcp_client = dhcp->dhcp_client;
2444 g_dhcp_client_clear_requests(dhcp_client);
2446 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2447 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
2448 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
2449 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
2451 g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL);
2452 g_dhcpv6_client_set_pd(dhcp_client, &T1, &T2, dhcp->prefixes);
2454 clear_callbacks(dhcp_client);
2456 g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
2457 request_pd_cb, dhcp);
2459 return g_dhcp_client_start(dhcp_client, NULL);
2462 static void advertise_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2464 struct connman_dhcpv6 *dhcp = user_data;
2466 DBG("dhcpv6 advertise pd msg %p", dhcp);
2470 g_dhcpv6_client_clear_retransmit(dhcp_client);
2472 if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
2474 dhcp->callback(dhcp->network,
2475 CONNMAN_DHCPV6_STATUS_FAIL, NULL);
2479 dhcp->RT = REQ_TIMEOUT * (1 + get_random());
2480 DBG("request initial RT timeout %d msec", dhcp->RT);
2481 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_pd_request, dhcp);
2483 dhcp->request_count = 1;
2485 dhcpv6_pd_request(dhcp);
2488 static void solicitation_pd_cb(GDHCPClient *dhcp_client, gpointer user_data)
2491 * This callback is here so that g_dhcp_client_start()
2492 * will enter the proper L3 mode.
2494 DBG("DHCPv6 %p solicitation msg received, ignoring it", user_data);
2497 static int dhcpv6_pd_solicitation(struct connman_dhcpv6 *dhcp)
2499 GDHCPClient *dhcp_client;
2502 DBG("dhcp %p", dhcp);
2504 dhcp_client = create_pd_client(dhcp, &ret);
2510 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
2512 g_dhcpv6_client_set_pd(dhcp_client, NULL, NULL, NULL);
2514 clear_callbacks(dhcp_client);
2516 g_dhcp_client_register_event(dhcp_client,
2517 G_DHCP_CLIENT_EVENT_ADVERTISE,
2518 advertise_pd_cb, dhcp);
2520 g_dhcp_client_register_event(dhcp_client,
2521 G_DHCP_CLIENT_EVENT_SOLICITATION,
2522 solicitation_pd_cb, dhcp);
2524 dhcp->dhcp_client = dhcp_client;
2526 return g_dhcp_client_start(dhcp_client, NULL);
2529 static gboolean start_pd_solicitation(gpointer user_data)
2531 struct connman_dhcpv6 *dhcp = user_data;
2533 /* Set the retransmission timeout, RFC 3315 chapter 14 */
2534 dhcp->RT = SOL_TIMEOUT * (1 + get_random());
2536 DBG("solicit initial RT timeout %d msec", dhcp->RT);
2538 dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
2540 dhcpv6_pd_solicitation(dhcp);
2545 int __connman_dhcpv6_start_pd(int index, GSList *prefixes, dhcpv6_cb callback)
2547 struct connman_service *service;
2548 struct connman_network *network;
2549 struct connman_dhcpv6 *dhcp;
2554 DBG("index %d", index);
2556 service = __connman_service_lookup_from_index(index);
2560 network = __connman_service_get_network(service);
2564 if (network_pd_table) {
2565 dhcp = g_hash_table_lookup(network_pd_table, network);
2566 if (dhcp && dhcp->started)
2570 dhcp = g_try_new0(struct connman_dhcpv6, 1);
2574 dhcp->network = network;
2575 dhcp->callback = callback;
2576 dhcp->started = true;
2580 * Try to load the earlier prefixes if caller did not supply
2581 * any that we could use.
2583 struct connman_ipconfig *ipconfig;
2584 ipconfig = __connman_service_get_ip6config(service);
2586 dhcp->prefixes = load_prefixes(ipconfig);
2588 dhcp->prefixes = prefixes;
2590 connman_network_ref(network);
2592 DBG("replace network %p dhcp %p", network, dhcp);
2594 g_hash_table_replace(network_pd_table, network, dhcp);
2596 if (!dhcp->prefixes) {
2598 * Refresh start, try to get prefixes.
2600 start_pd_solicitation(dhcp);
2603 * We used to have prefixes, try to use them again.
2604 * We need to use timeouts from confirm msg, RFC 3633, ch 12.1
2606 start_pd_rebind_with_confirm(dhcp);
2612 void __connman_dhcpv6_stop_pd(int index)
2614 struct connman_service *service;
2615 struct connman_network *network;
2620 DBG("index %d", index);
2622 if (!network_pd_table)
2625 service = __connman_service_lookup_from_index(index);
2629 network = __connman_service_get_network(service);
2633 __connman_dhcpv6_start_pd_release(network, NULL);
2635 if (g_hash_table_remove(network_pd_table, network))
2636 connman_network_unref(network);
2639 int __connman_dhcpv6_init(void)
2645 network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2646 NULL, remove_network);
2648 network_pd_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2649 NULL, remove_network);
2654 void __connman_dhcpv6_cleanup(void)
2658 g_hash_table_destroy(network_table);
2659 network_table = NULL;
2661 g_hash_table_destroy(network_pd_table);
2662 network_pd_table = NULL;