connection: Separate IPv4 and IPv6 gateway and routing handling.
authorJukka Rissanen <jukka.rissanen@linux.intel.com>
Wed, 22 Jun 2011 10:37:44 +0000 (13:37 +0300)
committerSamuel Ortiz <sameo@linux.intel.com>
Mon, 27 Jun 2011 14:15:08 +0000 (16:15 +0200)
include/ipconfig.h
src/connection.c
src/connman.h
src/ipconfig.c
src/network.c

index a0673af..8980a78 100644 (file)
@@ -58,6 +58,7 @@ void connman_ipaddress_copy(struct connman_ipaddress *ipaddress,
 
 enum connman_ipconfig_type {
        CONNMAN_IPCONFIG_TYPE_UNKNOWN = 0,
+       CONNMAN_IPCONFIG_TYPE_ALL     = 0,
        CONNMAN_IPCONFIG_TYPE_IPV4    = 1,
        CONNMAN_IPCONFIG_TYPE_IPV6    = 2,
 };
index 10ef04a..a402588 100644 (file)
 
 #include "connman.h"
 
-struct gateway_data {
-       int index;
-       struct connman_service *service;
-       char *ipv4_gateway;
-       char *ipv6_gateway;
-       unsigned int order;
+struct gateway_config {
        gboolean active;
+       char *gateway;
+
        /* VPN extra data */
        gboolean vpn;
        char *vpn_ip;
        int vpn_phy_index;
 };
 
+struct gateway_data {
+       int index;
+       struct connman_service *service;
+       unsigned int order;
+       struct gateway_config *ipv4_gateway;
+       struct gateway_config *ipv6_gateway;
+};
+
 static GHashTable *gateway_hash = NULL;
 
-static struct gateway_data *find_gateway(int index, const char *gateway)
+static struct gateway_config *find_gateway(int index, const char *gateway)
 {
        GHashTableIter iter;
        gpointer value, key;
@@ -59,46 +64,113 @@ static struct gateway_data *find_gateway(int index, const char *gateway)
        while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
                struct gateway_data *data = value;
 
-               if (data->ipv4_gateway == NULL)
-                       continue;
+               if (data->ipv4_gateway != NULL && data->index == index &&
+                               g_str_equal(data->ipv4_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data->ipv4_gateway;
 
-               if (data->index == index &&
-                               g_str_equal(data->ipv4_gateway, gateway)
-                                                               == TRUE)
-                       return data;
+               if (data->ipv6_gateway != NULL && data->index == index &&
+                               g_str_equal(data->ipv6_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data->ipv6_gateway;
        }
 
        return NULL;
 }
 
-static int del_routes(struct gateway_data *data)
+static int del_routes(struct gateway_data *data,
+                       enum connman_ipconfig_type type)
 {
-       if (data->vpn) {
-               if (data->vpn_phy_index >= 0)
-                       connman_inet_del_host_route(data->vpn_phy_index,
-                                                       data->ipv4_gateway);
-               return connman_inet_clear_gateway_address(data->index,
-                                                       data->vpn_ip);
-       } else if (g_strcmp0(data->ipv4_gateway, "0.0.0.0") == 0) {
-               return connman_inet_clear_gateway_interface(data->index);
-       } else {
-               connman_inet_del_ipv6_host_route(data->index,
-                                               data->ipv6_gateway);
-               connman_inet_clear_ipv6_gateway_address(data->index,
-                                                       data->ipv6_gateway);
-               connman_inet_del_host_route(data->index, data->ipv4_gateway);
-               return connman_inet_clear_gateway_address(data->index,
-                                                       data->ipv4_gateway);
+       int status4 = 0, status6 = 0;
+       int do_ipv4 = FALSE, do_ipv6 = FALSE;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               do_ipv4 = TRUE;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               do_ipv6 = TRUE;
+       else
+               do_ipv4 = do_ipv6 = TRUE;
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL) {
+               if (data->ipv4_gateway->vpn == TRUE) {
+                       if (data->ipv4_gateway->vpn_phy_index >= 0)
+                               connman_inet_del_host_route(
+                                       data->ipv4_gateway->vpn_phy_index,
+                                       data->ipv4_gateway->gateway);
+
+                       status4 = connman_inet_clear_gateway_address(
+                                               data->index,
+                                               data->ipv4_gateway->vpn_ip);
+
+               } else if (g_strcmp0(data->ipv4_gateway->gateway,
+                                                       "0.0.0.0") == 0) {
+                       status4 = connman_inet_clear_gateway_interface(
+                                                               data->index);
+               } else {
+                       connman_inet_del_host_route(data->index,
+                                               data->ipv4_gateway->gateway);
+                       status4 = connman_inet_clear_gateway_address(
+                                               data->index,
+                                               data->ipv4_gateway->gateway);
+               }
+       }
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL) {
+               if (data->ipv6_gateway->vpn == TRUE) {
+                       if (data->ipv6_gateway->vpn_phy_index >= 0)
+                               connman_inet_del_host_route(
+                                       data->ipv6_gateway->vpn_phy_index,
+                                       data->ipv6_gateway->gateway);
+
+                       status6 = connman_inet_clear_ipv6_gateway_address(
+                                               data->index,
+                                               data->ipv6_gateway->vpn_ip);
+
+               } else if (g_strcmp0(data->ipv6_gateway->gateway, "::") == 0) {
+                       status6 = connman_inet_clear_ipv6_gateway_interface(
+                                                               data->index);
+               } else {
+                       connman_inet_del_ipv6_host_route(data->index,
+                                               data->ipv6_gateway->gateway);
+                       status6 = connman_inet_clear_ipv6_gateway_address(
+                                               data->index,
+                                               data->ipv6_gateway->gateway);
+               }
        }
+
+       return (status4 < 0 ? status4 : status6);
+}
+
+static int disable_gateway(struct gateway_data *data,
+                       enum connman_ipconfig_type type)
+{
+       gboolean active = FALSE;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               if (data->ipv4_gateway != NULL)
+                       active = data->ipv4_gateway->active;
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+               if (data->ipv6_gateway != NULL)
+                       active = data->ipv6_gateway->active;
+       } else
+               active = TRUE;
+
+       DBG("type %d active %d", type, active);
+
+       if (active == TRUE)
+               return del_routes(data, type);
+
+       return 0;
 }
 
 static struct gateway_data *add_gateway(struct connman_service *service,
-                                       int index, const char *ipv4_gateway,
-                                       const char *ipv6_gateway)
+                                       int index, const char *gateway,
+                                       enum connman_ipconfig_type type)
 {
-       struct gateway_data *data;
+       struct gateway_data *data, *old;
+       struct gateway_config *config;
 
-       if (strlen(ipv4_gateway) == 0)
+       if (gateway == NULL || strlen(gateway) == 0)
                return NULL;
 
        data = g_try_new0(struct gateway_data, 1);
@@ -106,16 +178,54 @@ static struct gateway_data *add_gateway(struct connman_service *service,
                return NULL;
 
        data->index = index;
-       data->ipv4_gateway = g_strdup(ipv4_gateway);
-       data->ipv6_gateway = g_strdup(ipv6_gateway);
-       data->active = FALSE;
-       data->vpn_ip = NULL;
-       data->vpn = FALSE;
-       data->vpn_phy_index = -1;
+
+       config = g_try_new0(struct gateway_config, 1);
+       if (config == NULL) {
+               g_free(data);
+               return NULL;
+       }
+
+       config->gateway = g_strdup(gateway);
+       config->vpn_ip = NULL;
+       config->vpn = FALSE;
+       config->vpn_phy_index = -1;
+       config->active = FALSE;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               data->ipv4_gateway = config;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               data->ipv6_gateway = config;
+       else {
+               g_free(config->gateway);
+               g_free(config);
+               g_free(data);
+               return NULL;
+       }
+
        data->service = service;
 
        data->order = __connman_service_get_order(service);
 
+       /*
+        * If the service is already in the hash, then we
+        * must not replace it blindly but disable the gateway
+        * of the type we are replacing and take the other type
+        * from old gateway settings.
+        */
+       old = g_hash_table_lookup(gateway_hash, service);
+       if (old != NULL) {
+               DBG("Replacing gw %p ipv4 %p ipv6 %p", old,
+                       old->ipv4_gateway, old->ipv6_gateway);
+               disable_gateway(old, type);
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+                       data->ipv6_gateway = old->ipv6_gateway;
+                       old->ipv6_gateway = NULL;
+               } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+                       data->ipv4_gateway = old->ipv4_gateway;
+                       old->ipv4_gateway = NULL;
+               }
+       }
+
        g_hash_table_replace(gateway_hash, service, data);
 
        return data;
@@ -123,27 +233,50 @@ static struct gateway_data *add_gateway(struct connman_service *service,
 
 static void connection_newgateway(int index, const char *gateway)
 {
-       struct gateway_data *data;
+       struct gateway_config *config;
 
        DBG("index %d gateway %s", index, gateway);
 
-       data = find_gateway(index, gateway);
-       if (data == NULL)
+       config = find_gateway(index, gateway);
+       if (config == NULL)
                return;
 
-       data->active = TRUE;
+       config->active = TRUE;
 }
 
-static void set_default_gateway(struct gateway_data *data)
+static void set_default_gateway(struct gateway_data *data,
+                               enum connman_ipconfig_type type)
 {
        int index;
+       int status4 = 0, status6 = 0;
+       int do_ipv4 = FALSE, do_ipv6 = FALSE;
 
-       DBG("gateway %s", data->ipv4_gateway);
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               do_ipv4 = TRUE;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               do_ipv6 = TRUE;
+       else
+               do_ipv4 = do_ipv6 = TRUE;
 
-       if (data->vpn == TRUE) {
+       DBG("type %d gateway ipv4 %p ipv6 %p", type, data->ipv4_gateway,
+                                               data->ipv6_gateway);
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
+                                       data->ipv4_gateway->vpn == TRUE) {
                connman_inet_set_gateway_address(data->index,
-                                                       data->vpn_ip);
-               data->active = TRUE;
+                                               data->ipv4_gateway->vpn_ip);
+               data->ipv4_gateway->active = TRUE;
+
+               __connman_service_indicate_default(data->service);
+
+               return;
+       }
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
+                                       data->ipv6_gateway->vpn == TRUE) {
+               connman_inet_set_ipv6_gateway_address(data->index,
+                                               data->ipv6_gateway->vpn_ip);
+               data->ipv6_gateway->active = TRUE;
 
                __connman_service_indicate_default(data->service);
 
@@ -152,14 +285,31 @@ static void set_default_gateway(struct gateway_data *data)
 
        index = __connman_service_get_index(data->service);
 
-       if (g_strcmp0(data->ipv4_gateway, "0.0.0.0") == 0) {
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
+                       g_strcmp0(data->ipv4_gateway->gateway,
+                                                       "0.0.0.0") == 0) {
                if (connman_inet_set_gateway_interface(index) < 0)
                        return;
                goto done;
        }
 
-       connman_inet_set_ipv6_gateway_address(index, data->ipv6_gateway);
-       if (connman_inet_set_gateway_address(index, data->ipv4_gateway) < 0)
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
+                       g_strcmp0(data->ipv6_gateway->gateway,
+                                                       "::") == 0) {
+               if (connman_inet_set_ipv6_gateway_interface(index) < 0)
+                       return;
+               goto done;
+       }
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
+               status6 = connman_inet_set_ipv6_gateway_address(index,
+                                               data->ipv6_gateway->gateway);
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
+               status4 = connman_inet_set_gateway_address(index,
+                                               data->ipv4_gateway->gateway);
+
+       if (status4 < 0 || status6 < 0)
                return;
 
 done:
@@ -187,39 +337,41 @@ static struct gateway_data *find_default_gateway(void)
        return found;
 }
 
-static int disable_gateway(struct gateway_data *data)
-{
-       if (data->active == TRUE)
-               return del_routes(data);
-
-       return 0;
-}
-
 static void remove_gateway(gpointer user_data)
 {
        struct gateway_data *data = user_data;
 
-       DBG("gateway %s", data->ipv4_gateway);
+       DBG("gateway ipv4 %p ipv6 %p", data->ipv4_gateway, data->ipv6_gateway);
+
+       if (data->ipv4_gateway != NULL) {
+               g_free(data->ipv4_gateway->gateway);
+               g_free(data->ipv4_gateway->vpn_ip);
+               g_free(data->ipv4_gateway);
+       }
+
+       if (data->ipv6_gateway != NULL) {
+               g_free(data->ipv6_gateway->gateway);
+               g_free(data->ipv6_gateway->vpn_ip);
+               g_free(data->ipv6_gateway);
+       }
 
-       g_free(data->ipv4_gateway);
-       g_free(data->ipv6_gateway);
-       g_free(data->vpn_ip);
        g_free(data);
 }
 
 static void connection_delgateway(int index, const char *gateway)
 {
+       struct gateway_config *config;
        struct gateway_data *data;
 
        DBG("index %d gateway %s", index, gateway);
 
-       data = find_gateway(index, gateway);
-       if (data != NULL)
-               data->active = FALSE;
+       config = find_gateway(index, gateway);
+       if (config != NULL)
+               config->active = FALSE;
 
        data = find_default_gateway();
        if (data != NULL)
-               set_default_gateway(data);
+               set_default_gateway(data, CONNMAN_IPCONFIG_TYPE_ALL);
 }
 
 static struct connman_rtnl connection_rtnl = {
@@ -240,7 +392,12 @@ static struct gateway_data *find_active_gateway(void)
        while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
                struct gateway_data *data = value;
 
-               if (data->active == TRUE)
+               if (data->ipv4_gateway != NULL &&
+                               data->ipv4_gateway->active == TRUE)
+                       return data;
+
+               if (data->ipv6_gateway != NULL &&
+                               data->ipv6_gateway->active == TRUE)
                        return data;
        }
 
@@ -263,9 +420,27 @@ static void update_order(void)
        }
 }
 
+void __connman_connection_gateway_activate(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
+{
+       struct gateway_data *data = NULL;
+
+       data = g_hash_table_lookup(gateway_hash, service);
+       if (data == NULL)
+               return;
+
+       DBG("gateway %p/%p type %d", data->ipv4_gateway,
+                                       data->ipv6_gateway, type);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               data->ipv4_gateway->active = TRUE;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               data->ipv6_gateway->active = TRUE;
+}
+
 int __connman_connection_gateway_add(struct connman_service *service,
-                                       const char *ipv4_gateway,
-                                       const char *ipv6_gateway,
+                                       const char *gateway,
+                                       enum connman_ipconfig_type type,
                                        const char *peer)
 {
        struct gateway_data *active_gateway = NULL;
@@ -274,76 +449,127 @@ int __connman_connection_gateway_add(struct connman_service *service,
 
        index = __connman_service_get_index(service);
 
-       DBG("service %p index %d ipv4 gateway %s ipv6 gateway %s vpn ip %s",
-               service, index, ipv4_gateway, ipv6_gateway, peer);
-
-       /*
-        * If gateway is NULL, it's a point to point link and the default
-        * gateway is 0.0.0.0, meaning the interface.
-        */
-       if (ipv4_gateway == NULL)
-               ipv4_gateway = "0.0.0.0";
+       DBG("service %p index %d gateway %s vpn ip %s type %d",
+               service, index, gateway, peer, type);
 
        active_gateway = find_active_gateway();
-       new_gateway = add_gateway(service, index, ipv4_gateway, ipv6_gateway);
+       new_gateway = add_gateway(service, index, gateway, type);
        if (new_gateway == NULL)
                return 0;
 
-       if (new_gateway->ipv6_gateway) {
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                       new_gateway->ipv6_gateway != NULL &&
+                       g_strcmp0(new_gateway->ipv6_gateway->gateway,
+                                                               "::") != 0)
                connman_inet_add_ipv6_host_route(index,
-                                               new_gateway->ipv6_gateway,
-                                               NULL);
-       }
+                                       new_gateway->ipv6_gateway->gateway,
+                                       NULL);
 
-       if (g_strcmp0(new_gateway->ipv4_gateway, "0.0.0.0") != 0) {
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                       new_gateway->ipv4_gateway != NULL &&
+                       g_strcmp0(new_gateway->ipv4_gateway->gateway,
+                                                       "0.0.0.0") != 0)
                connman_inet_add_host_route(index,
-                                               new_gateway->ipv4_gateway,
-                                               NULL);
+                                       new_gateway->ipv4_gateway->gateway,
+                                       NULL);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                               new_gateway->ipv4_gateway != NULL) {
+               __connman_service_nameserver_add_routes(service,
+                                       new_gateway->ipv4_gateway->gateway);
+               __connman_service_set_ipconfig_ready(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
        }
 
-       __connman_service_nameserver_add_routes(service,
-                                               new_gateway->ipv4_gateway);
-
-       __connman_service_set_ipconfig_ready(service, CONNMAN_IPCONFIG_TYPE_IPV4);
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                               new_gateway->ipv6_gateway != NULL) {
+               __connman_service_nameserver_add_routes(service,
+                                       new_gateway->ipv6_gateway->gateway);
+               __connman_service_set_ipconfig_ready(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+       }
 
        if (connman_service_get_type(service) == CONNMAN_SERVICE_TYPE_VPN) {
-               new_gateway->vpn = TRUE;
-               if (peer != NULL)
-                       new_gateway->vpn_ip = g_strdup(peer);
-               else if (ipv4_gateway != NULL)
-                       new_gateway->vpn_ip = g_strdup(ipv4_gateway);
-               else
-                       new_gateway->vpn_ip = g_strdup(ipv6_gateway);
-
-               if (active_gateway)
-                       new_gateway->vpn_phy_index = active_gateway->index;
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                                       new_gateway->ipv4_gateway != NULL) {
+                       new_gateway->ipv4_gateway->vpn = TRUE;
+                       if (peer != NULL)
+                               new_gateway->ipv4_gateway->vpn_ip =
+                                                       g_strdup(peer);
+                       else if (gateway != NULL)
+                               new_gateway->ipv4_gateway->vpn_ip =
+                                                       g_strdup(gateway);
+                       if (active_gateway)
+                               new_gateway->ipv4_gateway->vpn_phy_index =
+                                                       active_gateway->index;
+
+               } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                                       new_gateway->ipv6_gateway != NULL) {
+                       new_gateway->ipv6_gateway->vpn = TRUE;
+                       if (peer != NULL)
+                               new_gateway->ipv6_gateway->vpn_ip =
+                                                       g_strdup(peer);
+                       else if (gateway != NULL)
+                               new_gateway->ipv6_gateway->vpn_ip =
+                                                       g_strdup(gateway);
+                       if (active_gateway)
+                               new_gateway->ipv6_gateway->vpn_phy_index =
+                                                       active_gateway->index;
+               }
        } else {
-               new_gateway->vpn = FALSE;
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                                       new_gateway->ipv4_gateway != NULL)
+                       new_gateway->ipv4_gateway->vpn = FALSE;
+
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                                       new_gateway->ipv6_gateway != NULL)
+                       new_gateway->ipv6_gateway->vpn = FALSE;
        }
 
        if (active_gateway == NULL) {
-               set_default_gateway(new_gateway);
+               set_default_gateway(new_gateway, type);
                return 0;
        }
 
-       if (new_gateway->vpn == TRUE) {
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                               new_gateway->ipv4_gateway != NULL &&
+                               new_gateway->ipv4_gateway->vpn == TRUE) {
                connman_inet_add_host_route(active_gateway->index,
-                                               new_gateway->ipv4_gateway,
-                                               active_gateway->ipv4_gateway);
+                                       new_gateway->ipv4_gateway->gateway,
+                                       active_gateway->ipv4_gateway->gateway);
                connman_inet_clear_gateway_address(active_gateway->index,
-                                               active_gateway->ipv4_gateway);
+                                       active_gateway->ipv4_gateway->gateway);
+       }
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                               new_gateway->ipv6_gateway != NULL &&
+                               new_gateway->ipv6_gateway->vpn == TRUE) {
+               connman_inet_add_ipv6_host_route(active_gateway->index,
+                                       new_gateway->ipv6_gateway->gateway,
+                                       active_gateway->ipv6_gateway->gateway);
+               connman_inet_clear_ipv6_gateway_address(active_gateway->index,
+                                       active_gateway->ipv6_gateway->gateway);
        }
 
        return 0;
 }
 
-void __connman_connection_gateway_remove(struct connman_service *service)
+void __connman_connection_gateway_remove(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
 {
        struct gateway_data *data = NULL;
-       gboolean set_default = FALSE;
+       gboolean set_default4 = FALSE, set_default6 = FALSE;
+       int do_ipv4 = FALSE, do_ipv6 = FALSE;
        int err;
 
-       DBG("service %p", service);
+       DBG("service %p type %d", service, type);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               do_ipv4 = TRUE;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               do_ipv6 = TRUE;
+       else
+               do_ipv4 = do_ipv6 = TRUE;
 
        __connman_service_nameserver_del_routes(service);
 
@@ -351,27 +577,56 @@ void __connman_connection_gateway_remove(struct connman_service *service)
        if (data == NULL)
                return;
 
-       set_default = data->vpn;
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
+               set_default4 = data->ipv4_gateway->vpn;
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
+               set_default6 = data->ipv6_gateway->vpn;
+
+       DBG("ipv4 gateway %s ipv6 gateway %s vpn %d/%d",
+               data->ipv4_gateway ? data->ipv4_gateway->gateway : "<null>",
+               data->ipv6_gateway ? data->ipv6_gateway->gateway : "<null>",
+               set_default4, set_default6);
 
-       if (data->vpn == TRUE && data->index >= 0) {
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
+                       data->ipv4_gateway->vpn == TRUE && data->index >= 0)
                connman_inet_del_host_route(data->index,
-                                               data->ipv4_gateway);
-       }
+                                               data->ipv4_gateway->gateway);
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
+                       data->ipv6_gateway->vpn == TRUE && data->index >= 0)
+               connman_inet_del_ipv6_host_route(data->index,
+                                               data->ipv6_gateway->gateway);
 
        __connman_service_nameserver_del_routes(service);
 
-       err = disable_gateway(data);
-       g_hash_table_remove(gateway_hash, service);
+       err = disable_gateway(data, type);
+
+       /*
+        * We remove the service from the hash only if all the gateway
+        * settings are to be removed.
+        */
+       if (do_ipv4 == do_ipv6 ||
+               (data->ipv4_gateway != NULL && data->ipv6_gateway == NULL
+                       && do_ipv4 == TRUE) ||
+               (data->ipv6_gateway != NULL && data->ipv4_gateway == NULL
+                       && do_ipv6 == TRUE)
+               )
+               g_hash_table_remove(gateway_hash, service);
+       else
+               DBG("Not yet removing gw ipv4 %p/%d ipv6 %p/%d",
+                       data->ipv4_gateway, do_ipv4,
+                       data->ipv6_gateway, do_ipv6);
 
        /* with vpn this will be called after the network was deleted,
         * we need to call set_default here because we will not recieve any
         * gateway delete notification.
         * We hit the same issue if remove_gateway() fails.
         */
-       if (set_default || err < 0) {
+       if (set_default4 || set_default6 || err < 0) {
                data = find_default_gateway();
                if (data != NULL)
-                       set_default_gateway(data);
+                       set_default_gateway(data, type);
        }
 }
 
@@ -424,7 +679,7 @@ void __connman_connection_cleanup(void)
        while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
                struct gateway_data *data = value;
 
-               disable_gateway(data);
+               disable_gateway(data, CONNMAN_IPCONFIG_TYPE_ALL);
        }
 
        g_hash_table_destroy(gateway_hash);
index a6361d7..0d6ca8b 100644 (file)
@@ -286,12 +286,15 @@ int __connman_connection_init(void);
 void __connman_connection_cleanup(void);
 
 int __connman_connection_gateway_add(struct connman_service *service,
-                                       const char *ipv4_gateway,
-                                       const char *ipv6_gateway,
+                                       const char *gateway,
+                                       enum connman_ipconfig_type type,
                                        const char *peer);
-void __connman_connection_gateway_remove(struct connman_service *service);
+void __connman_connection_gateway_remove(struct connman_service *service,
+                                       enum connman_ipconfig_type type);
 
 gboolean __connman_connection_update_gateway(void);
+void __connman_connection_gateway_activate(struct connman_service *service,
+                                       enum connman_ipconfig_type type);
 
 int __connman_wpad_init(void);
 void __connman_wpad_cleanup(void);
index 700f4a6..c8466d9 100644 (file)
@@ -1098,17 +1098,17 @@ int __connman_ipconfig_gateway_add(struct connman_ipconfig *ipconfig)
        if (service == NULL)
                return -EINVAL;
 
-       __connman_connection_gateway_remove(service);
+       __connman_connection_gateway_remove(service, ipconfig->type);
 
-       if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6) {
-               return __connman_connection_gateway_add(service, NULL,
-                                               ipconfig->address->gateway,
-                                                       ipconfig->address->peer);
-       } else if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+       DBG("type %d gw %s peer %s", ipconfig->type,
+               ipconfig->address->gateway, ipconfig->address->peer);
+
+       if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6 ||
+                               ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV4)
                return __connman_connection_gateway_add(service,
                                                ipconfig->address->gateway,
-                                                       NULL, ipconfig->address->peer);
-       }
+                                               ipconfig->type,
+                                               ipconfig->address->peer);
 
        return 0;
 }
@@ -1121,7 +1121,7 @@ void __connman_ipconfig_gateway_remove(struct connman_ipconfig *ipconfig)
 
        service = __connman_service_lookup_from_index(ipconfig->index);
        if (service != NULL)
-               __connman_connection_gateway_remove(service);
+               __connman_connection_gateway_remove(service, ipconfig->type);
 }
 
 unsigned char __connman_ipconfig_get_prefixlen(struct connman_ipconfig *ipconfig)
index 1693109..acc374f 100644 (file)
@@ -1083,7 +1083,8 @@ static gboolean set_connected(gpointer user_data)
                                        CONNMAN_SERVICE_STATE_DISCONNECT,
                                        CONNMAN_IPCONFIG_TYPE_IPV6);
 
-               __connman_connection_gateway_remove(service);
+               __connman_connection_gateway_remove(service,
+                                               CONNMAN_IPCONFIG_TYPE_ALL);
 
                __connman_service_indicate_state(service,
                                        CONNMAN_SERVICE_STATE_IDLE,