dhcpv6: Release message implemented.
authorJukka Rissanen <jukka.rissanen@linux.intel.com>
Thu, 5 Jan 2012 11:38:14 +0000 (13:38 +0200)
committerDaniel Wagner <daniel.wagner@bmw-carit.de>
Thu, 5 Jan 2012 12:17:26 +0000 (13:17 +0100)
gdhcp/client.c
gdhcp/gdhcp.h
src/connman.h
src/dhcpv6.c
src/network.c

index 7743aa5..99f9951 100644 (file)
@@ -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;
index 49b06b1..edebc9e 100644 (file)
@@ -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 {
index d99d4b4..5dc588a 100644 (file)
@@ -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);
index fd6e0eb..f2c7da6 100644 (file)
@@ -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);
 
index bff0e7e..2637ba5 100644 (file)
@@ -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;
                }