*
* Connection Manager
*
- * Copyright (C) 2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
GSList *prefixes; /* network prefixes from radvd */
int request_count; /* how many times REQUEST have been sent */
gboolean stateless; /* TRUE if stateless DHCPv6 is used */
+ gboolean started; /* TRUE if we have DHCPv6 started */
};
static GHashTable *network_table;
static int dhcpv6_request(struct connman_dhcpv6 *dhcp, gboolean add_addresses);
+static void clear_timer(struct connman_dhcpv6 *dhcp)
+{
+ if (dhcp->timeout > 0) {
+ g_source_remove(dhcp->timeout);
+ dhcp->timeout = 0;
+ }
+}
+
static inline float get_random()
{
return (rand() % 200 - 100) / 1000.0;
dhcp->nameservers = NULL;
dhcp->timeservers = NULL;
+ dhcp->started = FALSE;
g_slist_foreach(dhcp->prefixes, free_prefix, NULL);
g_slist_free(dhcp->prefixes);
dhcp->nameservers = nameservers;
- for (i = 0; dhcp->nameservers[i] != NULL; i++)
+ for (i = 0; dhcp->nameservers != NULL &&
+ dhcp->nameservers[i] != NULL; i++)
__connman_service_nameserver_append(service,
dhcp->nameservers[i],
FALSE);
dhcp->timeservers = timeservers;
- for (i = 0; dhcp->timeservers[i] != NULL; i++)
+ for (i = 0; dhcp->timeservers != NULL &&
+ dhcp->timeservers[i] != NULL; i++)
__connman_service_timeserver_append(service,
dhcp->timeservers[i]);
} else
index = connman_network_get_index(dhcp->network);
dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
- if (error != G_DHCP_CLIENT_ERROR_NONE)
+ if (error != G_DHCP_CLIENT_ERROR_NONE) {
+ clear_timer(dhcp);
return -EINVAL;
+ }
if (getenv("CONNMAN_DHCPV6_DEBUG"))
g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
service = __connman_service_lookup_from_network(dhcp->network);
- if (service == NULL)
+ if (service == NULL) {
+ clear_timer(dhcp);
+ g_dhcp_client_unref(dhcp_client);
return -EINVAL;
+ }
ret = set_duid(service, dhcp->network, dhcp_client, index);
- if (ret < 0)
+ if (ret < 0) {
+ clear_timer(dhcp);
+ g_dhcp_client_unref(dhcp_client);
return ret;
+ }
g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
dhcp->nameservers = nameservers;
- for (i = 0; dhcp->nameservers[i] != NULL; i++)
+ for (i = 0; dhcp->nameservers != NULL &&
+ dhcp->nameservers[i] != NULL; i++)
__connman_service_nameserver_append(service,
dhcp->nameservers[i],
FALSE);
dhcp->timeservers = timeservers;
- for (i = 0; dhcp->timeservers[i] != NULL; i++)
+ for (i = 0; dhcp->timeservers != NULL &&
+ dhcp->timeservers[i] != NULL; i++)
__connman_service_timeserver_append(service,
dhcp->timeservers[i]);
} else
g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
NULL, NULL, &expired);
- current = time(0);
+ current = time(NULL);
if (current > expired) {
DBG("expired by %d secs", (int)(current - expired));
if (dhcp == NULL)
return -ENOENT;
- if (dhcp->timeout > 0) {
- g_source_remove(dhcp->timeout);
- dhcp->timeout = 0;
- }
+ DBG("network %p dhcp %p", network, dhcp);
+
+ clear_timer(dhcp);
g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
&last_renew, &last_rebind, &expired);
- current = time(0);
+ current = time(NULL);
DBG("T1 %u T2 %u expires %lu current %lu", T1, T2,
(unsigned long)expired, current);
return 0;
}
-static void release_cb(GDHCPClient *dhcp_client, gpointer user_data)
-{
- struct connman_dhcpv6 *dhcp = user_data;
-
- DBG("dhcpv6 release msg %p", dhcp);
-
- if (dhcp->callback != NULL) {
- uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
- dhcp->callback(dhcp->network, status == 0 ? TRUE : FALSE);
- }
-}
-
int __connman_dhcpv6_start_release(struct connman_network *network,
dhcp_cb callback)
{
if (dhcp == NULL)
return -ENOENT;
- DBG("dhcp %p stateless %d", dhcp, dhcp->stateless);
+ DBG("network %p dhcp %p client %p stateless %d", network, dhcp,
+ dhcp->dhcp_client, dhcp->stateless);
if (dhcp->stateless == TRUE)
return -EINVAL;
- if (dhcp->timeout > 0) {
- g_source_remove(dhcp->timeout);
- dhcp->timeout = 0;
- }
+ clear_timer(dhcp);
dhcp_client = dhcp->dhcp_client;
+ if (dhcp_client == NULL) {
+ /*
+ * We had started the DHCPv6 handshaking i.e., we have called
+ * __connman_dhcpv6_start() but it has not yet sent
+ * a solicitation message to server. This means that we do not
+ * have DHCPv6 configured yet so we can just quit here.
+ */
+ DBG("DHCPv6 was not started");
+ return 0;
+ }
g_dhcp_client_clear_requests(dhcp_client);
g_dhcp_client_clear_values(dhcp_client);
clear_callbacks(dhcp_client);
- g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RELEASE,
- release_cb, dhcp);
+ /*
+ * We do not register callback here because the answer might take too
+ * long time and network code might be in the middle of the disconnect.
+ * So we just inform the server that we are done with the addresses
+ * but ignore the reply from server. This is allowed by RFC 3315
+ * chapter 18.1.6.
+ */
dhcp->dhcp_client = dhcp_client;
{
DBG("dhcp %p", dhcp);
- if (dhcp->timeout > 0) {
- g_source_remove(dhcp->timeout);
- dhcp->timeout = 0;
- }
+ clear_timer(dhcp);
dhcpv6_free(dhcp);
DBG("");
+ if (network_table != NULL) {
+ dhcp = g_hash_table_lookup(network_table, network);
+ if (dhcp != NULL && dhcp->started == TRUE)
+ return -EBUSY;
+ }
+
dhcp = g_try_new0(struct connman_dhcpv6, 1);
if (dhcp == NULL)
return -ENOMEM;
dhcp->network = network;
dhcp->callback = callback;
dhcp->stateless = TRUE;
+ dhcp->started = TRUE;
connman_network_ref(network);
+ DBG("replace network %p dhcp %p", network, dhcp);
+
g_hash_table_replace(network_table, network, dhcp);
/* Initial timeout, RFC 3315, 18.1.5 */
DBG("dhcpv6 advertise msg %p", dhcp);
- if (dhcp->timeout > 0) {
- g_source_remove(dhcp->timeout);
- dhcp->timeout = 0;
- }
+ clear_timer(dhcp);
if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
if (dhcp->callback != NULL)
DBG("dhcpv6 solicitation msg %p", dhcp);
- if (dhcp->timeout > 0) {
- g_source_remove(dhcp->timeout);
- dhcp->timeout = 0;
- }
+ clear_timer(dhcp);
set_addresses(dhcp_client, dhcp);
}
index = connman_network_get_index(dhcp->network);
dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
- if (error != G_DHCP_CLIENT_ERROR_NONE)
+ if (error != G_DHCP_CLIENT_ERROR_NONE) {
+ clear_timer(dhcp);
return -EINVAL;
+ }
if (getenv("CONNMAN_DHCPV6_DEBUG"))
g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
service = __connman_service_lookup_from_network(dhcp->network);
- if (service == NULL)
+ if (service == NULL) {
+ clear_timer(dhcp);
+ g_dhcp_client_unref(dhcp_client);
return -EINVAL;
+ }
ret = set_duid(service, dhcp->network, dhcp_client, index);
- if (ret < 0)
+ if (ret < 0) {
+ clear_timer(dhcp);
+ g_dhcp_client_unref(dhcp_client);
return ret;
+ }
g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
DBG("");
+ if (network_table != NULL) {
+ dhcp = g_hash_table_lookup(network_table, network);
+ if (dhcp != NULL && dhcp->started == TRUE)
+ return -EBUSY;
+ }
+
dhcp = g_try_new0(struct connman_dhcpv6, 1);
if (dhcp == NULL)
return -ENOMEM;
dhcp->network = network;
dhcp->callback = callback;
dhcp->prefixes = prefixes;
+ dhcp->started = TRUE;
connman_network_ref(network);
+ DBG("replace network %p dhcp %p", network, dhcp);
+
g_hash_table_replace(network_table, network, dhcp);
/* Initial timeout, RFC 3315, 17.1.2 */
{
DBG("");
- srand(time(0));
+ srand(time(NULL));
network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, remove_network);