summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdhcp/client.c31
-rw-r--r--gdhcp/gdhcp.h1
-rw-r--r--src/connman.h2
-rw-r--r--src/dhcpv6.c64
-rw-r--r--src/network.c17
5 files changed, 114 insertions, 1 deletions
diff --git a/gdhcp/client.c b/gdhcp/client.c
index 7743aa50..99f99510 100644
--- a/gdhcp/client.c
+++ b/gdhcp/client.c
@@ -74,6 +74,7 @@ typedef enum _dhcp_client_state {
REQUEST,
RENEW,
REBIND,
+ RELEASE,
} ClientState;
struct _GDHCPClient {
@@ -126,6 +127,8 @@ struct _GDHCPClient {
gpointer renew_data;
GDHCPClientEventFunc rebind_cb;
gpointer rebind_data;
+ GDHCPClientEventFunc release_cb;
+ gpointer release_data;
char *last_address;
unsigned char *duid;
int duid_len;
@@ -817,6 +820,11 @@ static int send_dhcpv6_rebind(GDHCPClient *dhcp_client)
return send_dhcpv6_msg(dhcp_client, DHCPV6_REBIND, "rebind");
}
+static int send_dhcpv6_release(GDHCPClient *dhcp_client)
+{
+ return send_dhcpv6_msg(dhcp_client, DHCPV6_RELEASE, "release");
+}
+
static int send_information_req(GDHCPClient *dhcp_client)
{
return send_dhcpv6_msg(dhcp_client, DHCPV6_INFORMATION_REQ,
@@ -2002,6 +2010,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
case REQUEST:
case RENEW:
case REBIND:
+ case RELEASE:
if (dhcp_client->type != G_DHCP_IPV6)
return TRUE;
@@ -2053,6 +2062,11 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
dhcp_client->rebind_data);
return TRUE;
}
+ if (dhcp_client->release_cb != NULL) {
+ dhcp_client->release_cb(dhcp_client,
+ dhcp_client->release_data);
+ return TRUE;
+ }
break;
default:
break;
@@ -2196,6 +2210,16 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
return re;
}
send_dhcpv6_rebind(dhcp_client);
+
+ } else if (dhcp_client->release_cb) {
+ dhcp_client->state = RENEW;
+ re = switch_listening_mode(dhcp_client, L3);
+ if (re != 0) {
+ switch_listening_mode(dhcp_client, L_NONE);
+ dhcp_client->state = 0;
+ return re;
+ }
+ send_dhcpv6_release(dhcp_client);
}
return 0;
@@ -2346,6 +2370,12 @@ void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
dhcp_client->rebind_cb = func;
dhcp_client->rebind_data = data;
return;
+ case G_DHCP_CLIENT_EVENT_RELEASE:
+ if (dhcp_client->type == G_DHCP_IPV4)
+ return;
+ dhcp_client->release_cb = func;
+ dhcp_client->release_data = data;
+ return;
}
}
@@ -2386,6 +2416,7 @@ char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
case REQUEST:
case RENEW:
case REBIND:
+ case RELEASE:
break;
}
return NULL;
diff --git a/gdhcp/gdhcp.h b/gdhcp/gdhcp.h
index 49b06b1a..edebc9e1 100644
--- a/gdhcp/gdhcp.h
+++ b/gdhcp/gdhcp.h
@@ -58,6 +58,7 @@ typedef enum {
G_DHCP_CLIENT_EVENT_REQUEST,
G_DHCP_CLIENT_EVENT_RENEW,
G_DHCP_CLIENT_EVENT_REBIND,
+ G_DHCP_CLIENT_EVENT_RELEASE,
} GDHCPClientEvent;
typedef enum {
diff --git a/src/connman.h b/src/connman.h
index d99d4b4d..5dc588a6 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -286,6 +286,8 @@ int __connman_dhcpv6_start(struct connman_network *network,
GSList *prefixes, dhcp_cb callback);
int __connman_dhcpv6_start_renew(struct connman_network *network,
dhcp_cb callback);
+int __connman_dhcpv6_start_release(struct connman_network *network,
+ dhcp_cb callback);
int __connman_ipv4_init(void);
void __connman_ipv4_cleanup(void);
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index fd6e0ebe..f2c7da60 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -66,6 +66,7 @@ struct connman_dhcpv6 {
gboolean use_ta; /* set to TRUE if IPv6 privacy is enabled */
GSList *prefixes; /* network prefixes from radvd */
int request_count; /* how many times REQUEST have been sent */
+ gboolean stateless; /* TRUE if stateless DHCPv6 is used */
};
static GHashTable *network_table;
@@ -236,6 +237,10 @@ static void clear_callbacks(GDHCPClient *dhcp_client)
NULL, NULL);
g_dhcp_client_register_event(dhcp_client,
+ G_DHCP_CLIENT_EVENT_RELEASE,
+ NULL, NULL);
+
+ g_dhcp_client_register_event(dhcp_client,
G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
NULL, NULL);
}
@@ -854,6 +859,64 @@ int __connman_dhcpv6_start_renew(struct connman_network *network,
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)
+{
+ struct connman_dhcpv6 *dhcp;
+ GDHCPClient *dhcp_client;
+
+ if (network_table == NULL)
+ return 0; /* we are already released */
+
+ dhcp = g_hash_table_lookup(network_table, network);
+ if (dhcp == NULL)
+ return -ENOENT;
+
+ DBG("dhcp %p stateless %d", dhcp, dhcp->stateless);
+
+ if (dhcp->stateless == TRUE)
+ return -EINVAL;
+
+ if (dhcp->timeout > 0) {
+ g_source_remove(dhcp->timeout);
+ dhcp->timeout = 0;
+ }
+
+ dhcp_client = dhcp->dhcp_client;
+
+ g_dhcp_client_clear_requests(dhcp_client);
+ g_dhcp_client_clear_values(dhcp_client);
+
+ g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+ g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
+
+ g_dhcpv6_client_set_ia(dhcp_client,
+ connman_network_get_index(dhcp->network),
+ dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
+ NULL, NULL, TRUE);
+
+ clear_callbacks(dhcp_client);
+
+ g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RELEASE,
+ release_cb, dhcp);
+
+ dhcp->dhcp_client = dhcp_client;
+
+ return g_dhcp_client_start(dhcp_client, NULL);
+}
+
static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
{
DBG("dhcp %p", dhcp);
@@ -932,6 +995,7 @@ int __connman_dhcpv6_start_info(struct connman_network *network,
dhcp->network = network;
dhcp->callback = callback;
+ dhcp->stateless = TRUE;
connman_network_ref(network);
diff --git a/src/network.c b/src/network.c
index bff0e7e2..2637ba5d 100644
--- a/src/network.c
+++ b/src/network.c
@@ -974,6 +974,21 @@ static void stop_dhcpv6(struct connman_network *network)
__connman_dhcpv6_stop(network);
}
+static void dhcpv6_release_callback(struct connman_network *network,
+ connman_bool_t success)
+{
+ DBG("success %d", success);
+
+ stop_dhcpv6(network);
+}
+
+static void release_dhcpv6(struct connman_network *network)
+{
+ if (__connman_dhcpv6_start_release(network,
+ dhcpv6_release_callback) < 0)
+ stop_dhcpv6(network);
+}
+
static void dhcpv6_info_callback(struct connman_network *network,
connman_bool_t success)
{
@@ -1211,7 +1226,7 @@ static gboolean set_connected(gpointer user_data)
break;
case CONNMAN_IPCONFIG_METHOD_DHCP:
case CONNMAN_IPCONFIG_METHOD_AUTO:
- stop_dhcpv6(network);
+ release_dhcpv6(network);
break;
}