REQUEST,
RENEW,
REBIND,
+ RELEASE,
} ClientState;
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;
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,
case REQUEST:
case RENEW:
case REBIND:
+ case RELEASE:
if (dhcp_client->type != G_DHCP_IPV6)
return TRUE;
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;
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;
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;
}
}
case REQUEST:
case RENEW:
case REBIND:
+ case RELEASE:
break;
}
return NULL;
G_DHCP_CLIENT_EVENT_REQUEST,
G_DHCP_CLIENT_EVENT_RENEW,
G_DHCP_CLIENT_EVENT_REBIND,
+ G_DHCP_CLIENT_EVENT_RELEASE,
} GDHCPClientEvent;
typedef enum {
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);
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;
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);
}
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);
dhcp->network = network;
dhcp->callback = callback;
+ dhcp->stateless = TRUE;
connman_network_ref(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)
{
break;
case CONNMAN_IPCONFIG_METHOD_DHCP:
case CONNMAN_IPCONFIG_METHOD_AUTO:
- stop_dhcpv6(network);
+ release_dhcpv6(network);
break;
}