technology: return already enabled when tethering is enabled
[framework/connectivity/connman.git] / src / connection.c
index 477cf84..d40f428 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -49,6 +49,7 @@ struct gateway_data {
        unsigned int order;
        struct gateway_config *ipv4_gateway;
        struct gateway_config *ipv6_gateway;
+       connman_bool_t default_checked;
 };
 
 static GHashTable *gateway_hash = NULL;
@@ -80,6 +81,160 @@ static struct gateway_config *find_gateway(int index, const char *gateway)
        return NULL;
 }
 
+static struct gateway_data *lookup_gateway_data(struct gateway_config *config)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       if (config == NULL)
+               return NULL;
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               if (data->ipv4_gateway != NULL &&
+                               data->ipv4_gateway == config)
+                       return data;
+
+               if (data->ipv6_gateway != NULL &&
+                               data->ipv6_gateway == config)
+                       return data;
+       }
+
+       return NULL;
+}
+
+/*
+ * Find the gateway that is serving the VPN link
+ */
+static struct gateway_data *find_phy_gateway(int index, const char *gateway)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       if (gateway == NULL)
+               return NULL;
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               if (data->ipv4_gateway != NULL && data->index != index &&
+                               g_str_equal(data->ipv4_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data;
+
+               if (data->ipv6_gateway != NULL && data->index != index &&
+                               g_str_equal(data->ipv6_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data;
+       }
+
+       return NULL;
+}
+
+static void set_vpn_routes(struct gateway_data *new_gateway,
+                       struct connman_service *service,
+                       const char *gateway,
+                       enum connman_ipconfig_type type,
+                       const char *peer,
+                       struct gateway_data *active_gateway)
+{
+       struct gateway_config *config;
+       struct gateway_data *data;
+       struct connman_ipconfig *ipconfig;
+       char *dest;
+       int index;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               ipconfig = __connman_service_get_ip4config(service);
+               config = new_gateway->ipv4_gateway;
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+               ipconfig = __connman_service_get_ip6config(service);
+               config = new_gateway->ipv6_gateway;
+       } else
+               return;
+
+       if (config == NULL)
+               goto done;
+
+       config->vpn = TRUE;
+       if (peer != NULL)
+               config->vpn_ip = g_strdup(peer);
+       else if (gateway != NULL)
+               config->vpn_ip = g_strdup(gateway);
+
+       index = __connman_ipconfig_get_index(ipconfig);
+       data = find_phy_gateway(index, gateway);
+
+       if (data == NULL)
+               goto done;
+
+       /*
+        * data->service points now to original
+        * service that is serving the VPN link
+        */
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               ipconfig = __connman_service_get_ip4config(data->service);
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               ipconfig = __connman_service_get_ip6config(data->service);
+       else
+               return;
+
+       if (ipconfig != NULL) {
+               const char *address;
+
+               address = __connman_ipconfig_get_local(ipconfig);
+               config->vpn_phy_ip = g_strdup(address);
+       }
+
+       config->vpn_phy_index = data->index;
+
+       DBG("vpn %s phy %s index %d", config->vpn_ip,
+               config->vpn_phy_ip, config->vpn_phy_index);
+
+done:
+       if (active_gateway == NULL)
+               return;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               /*
+                * Special route to VPN server via gateway. This
+                * is needed so that we can access hosts behind
+                * the VPN. The route might already exist depending
+                * on network topology.
+                */
+               if (active_gateway->ipv4_gateway == NULL)
+                       return;
+
+               if (g_strcmp0(active_gateway->ipv4_gateway->gateway,
+                                                       "0.0.0.0") != 0)
+                       dest = active_gateway->ipv4_gateway->gateway;
+               else
+                       dest = NULL;
+
+               connman_inet_add_host_route(active_gateway->index, gateway,
+                                                                       dest);
+
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+
+               if (active_gateway->ipv6_gateway == NULL)
+                       return;
+
+               if (g_strcmp0(active_gateway->ipv6_gateway->gateway,
+                                                               "::") != 0)
+                       dest = active_gateway->ipv6_gateway->gateway;
+               else
+                       dest = NULL;
+
+               connman_inet_add_ipv6_host_route(active_gateway->index,
+                                                               gateway, dest);
+       }
+}
+
 static int del_routes(struct gateway_data *data,
                        enum connman_ipconfig_type type)
 {
@@ -95,11 +250,6 @@ static int del_routes(struct gateway_data *data,
 
        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);
@@ -119,11 +269,6 @@ static int del_routes(struct gateway_data *data,
 
        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);
@@ -227,27 +372,18 @@ static struct gateway_data *add_gateway(struct connman_service *service,
                        data->ipv4_gateway = old->ipv4_gateway;
                        old->ipv4_gateway = NULL;
                }
+       } else {
+               /*
+                * Only take a ref if we are adding new stuff to hash.
+                */
+               connman_service_ref(service);
        }
 
-       connman_service_ref(service);
        g_hash_table_replace(gateway_hash, service, data);
 
        return data;
 }
 
-static void connection_newgateway(int index, const char *gateway)
-{
-       struct gateway_config *config;
-
-       DBG("index %d gateway %s", index, gateway);
-
-       config = find_gateway(index, gateway);
-       if (config == NULL)
-               return;
-
-       config->active = TRUE;
-}
-
 static void set_default_gateway(struct gateway_data *data,
                                enum connman_ipconfig_type type)
 {
@@ -269,11 +405,15 @@ static void set_default_gateway(struct gateway_data *data,
                                        data->ipv4_gateway->vpn == TRUE) {
                connman_inet_set_gateway_address(data->index,
                                                data->ipv4_gateway->vpn_ip);
-               connman_inet_add_host_route(data->ipv4_gateway->vpn_phy_index,
-                                       data->ipv4_gateway->vpn_ip,
-                                       data->ipv4_gateway->vpn_phy_ip);
+               connman_inet_add_host_route(data->index,
+                                       data->ipv4_gateway->vpn_ip, NULL);
                data->ipv4_gateway->active = TRUE;
 
+               DBG("set %p index %d vpn %s index %d phy %s",
+                       data, data->index, data->ipv4_gateway->vpn_ip,
+                       data->ipv4_gateway->vpn_phy_index,
+                       data->ipv4_gateway->vpn_phy_ip);
+
                __connman_service_indicate_default(data->service);
 
                return;
@@ -283,12 +423,15 @@ static void set_default_gateway(struct gateway_data *data,
                                        data->ipv6_gateway->vpn == TRUE) {
                connman_inet_set_ipv6_gateway_address(data->index,
                                                data->ipv6_gateway->vpn_ip);
-               connman_inet_add_ipv6_host_route(
-                                       data->ipv6_gateway->vpn_phy_index,
-                                       data->ipv6_gateway->vpn_ip,
-                                       data->ipv6_gateway->vpn_phy_ip);
+               connman_inet_add_ipv6_host_route(data->index,
+                                       data->ipv6_gateway->vpn_ip, NULL);
                data->ipv6_gateway->active = TRUE;
 
+               DBG("set %p index %d vpn %s index %d phy %s",
+                       data, data->index, data->ipv6_gateway->vpn_ip,
+                       data->ipv6_gateway->vpn_phy_index,
+                       data->ipv6_gateway->vpn_phy_ip);
+
                __connman_service_indicate_default(data->service);
 
                return;
@@ -351,6 +494,11 @@ static void unset_default_gateway(struct gateway_data *data,
                                                data->ipv4_gateway->vpn_ip);
                data->ipv4_gateway->active = FALSE;
 
+               DBG("unset %p index %d vpn %s index %d phy %s",
+                       data, data->index, data->ipv4_gateway->vpn_ip,
+                       data->ipv4_gateway->vpn_phy_index,
+                       data->ipv4_gateway->vpn_phy_ip);
+
                return;
        }
 
@@ -362,6 +510,11 @@ static void unset_default_gateway(struct gateway_data *data,
                                                data->ipv6_gateway->vpn_ip);
                data->ipv6_gateway->active = FALSE;
 
+               DBG("unset %p index %d vpn %s index %d phy %s",
+                       data, data->index, data->ipv6_gateway->vpn_ip,
+                       data->ipv6_gateway->vpn_phy_index,
+                       data->ipv6_gateway->vpn_phy_ip);
+
                return;
        }
 
@@ -405,12 +558,119 @@ static struct gateway_data *find_default_gateway(void)
                if (found == NULL || data->order > order) {
                        found = data;
                        order = data->order;
+
+                       DBG("default %p order %d", found, order);
                }
        }
 
        return found;
 }
 
+static gboolean choose_default_gateway(struct gateway_data *data,
+                                       struct gateway_data *candidate)
+{
+       gboolean downgraded = FALSE;
+
+       /*
+        * If the current default is not active, then we mark
+        * this one as default. If the other one is already active
+        * we mark this one as non default.
+        */
+       if (data->ipv4_gateway != NULL) {
+               if (candidate->ipv4_gateway != NULL &&
+                               candidate->ipv4_gateway->active == FALSE) {
+                       DBG("ipv4 downgrading %p", candidate);
+                       unset_default_gateway(candidate,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+               }
+               if (candidate->ipv4_gateway != NULL &&
+                               candidate->ipv4_gateway->active == TRUE &&
+                               candidate->order > data->order) {
+                       DBG("ipv4 downgrading this %p", data);
+                       unset_default_gateway(data,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+                       downgraded = TRUE;
+               }
+       }
+
+       if (data->ipv6_gateway != NULL) {
+               if (candidate->ipv6_gateway != NULL &&
+                               candidate->ipv6_gateway->active == FALSE) {
+                       DBG("ipv6 downgrading %p", candidate);
+                       unset_default_gateway(candidate,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+               }
+
+               if (candidate->ipv6_gateway != NULL &&
+                               candidate->ipv6_gateway->active == TRUE &&
+                               candidate->order > data->order) {
+                       DBG("ipv6 downgrading this %p", data);
+                       unset_default_gateway(data,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+                       downgraded = TRUE;
+               }
+       }
+
+       return downgraded;
+}
+
+static void connection_newgateway(int index, const char *gateway)
+{
+       struct gateway_config *config;
+       struct gateway_data *data;
+       GHashTableIter iter;
+       gpointer value, key;
+       gboolean found = FALSE;
+
+       DBG("index %d gateway %s", index, gateway);
+
+       config = find_gateway(index, gateway);
+       if (config == NULL)
+               return;
+
+       config->active = TRUE;
+
+       /*
+        * It is possible that we have two default routes atm
+        * if there are two gateways waiting rtnl activation at the
+        * same time.
+        */
+       data = lookup_gateway_data(config);
+       if (data == NULL)
+               return;
+
+       if (data->default_checked == TRUE)
+               return;
+
+       /*
+        * The next checks are only done once, otherwise setting
+        * the default gateway could lead into rtnl forever loop.
+        */
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *candidate = value;
+
+               if (candidate == data)
+                       continue;
+
+               found = choose_default_gateway(data, candidate);
+               if (found == TRUE)
+                       break;
+       }
+
+       if (found == FALSE) {
+               if (data->ipv4_gateway != NULL)
+                       set_default_gateway(data, CONNMAN_IPCONFIG_TYPE_IPV4);
+
+               if (data->ipv6_gateway != NULL)
+                       set_default_gateway(data, CONNMAN_IPCONFIG_TYPE_IPV6);
+       }
+
+       data->default_checked = TRUE;
+}
+
 static void remove_gateway(gpointer user_data)
 {
        struct gateway_data *data = user_data;
@@ -514,6 +774,54 @@ void __connman_connection_gateway_activate(struct connman_service *service,
                data->ipv6_gateway->active = TRUE;
 }
 
+static void add_host_route(int family, int index, const char *gateway,
+                       enum connman_service_type service_type)
+{
+       switch (family) {
+       case AF_INET:
+               if (g_strcmp0(gateway, "0.0.0.0") != 0) {
+                       /*
+                        * We must not set route to the phy dev gateway in
+                        * VPN link. The packets to VPN link might be routed
+                        * back to itself and not routed into phy link gateway.
+                        */
+                       if (service_type != CONNMAN_SERVICE_TYPE_VPN)
+                               connman_inet_add_host_route(index, gateway,
+                                                                       NULL);
+               } else {
+                       /*
+                        * Add host route to P-t-P link so that services can
+                        * be moved around and we can have some link to P-t-P
+                        * network (although those P-t-P links have limited
+                        * usage if default route is not directed to them)
+                        */
+                       char *dest;
+                       if (connman_inet_get_dest_addr(index, &dest) == 0) {
+                               connman_inet_add_host_route(index, dest, NULL);
+                               g_free(dest);
+                       }
+               }
+               break;
+
+       case AF_INET6:
+               if (g_strcmp0(gateway, "::") != 0) {
+                       if (service_type != CONNMAN_SERVICE_TYPE_VPN)
+                               connman_inet_add_ipv6_host_route(index,
+                                                               gateway, NULL);
+               } else {
+                       /* P-t-P link, add route to destination */
+                       char *dest;
+                       if (connman_inet_ipv6_get_dest_addr(index,
+                                                               &dest) == 0) {
+                               connman_inet_add_ipv6_host_route(index, dest,
+                                                               NULL);
+                               g_free(dest);
+                       }
+               }
+               break;
+       }
+}
+
 int __connman_connection_gateway_add(struct connman_service *service,
                                        const char *gateway,
                                        enum connman_ipconfig_type type,
@@ -521,13 +829,14 @@ int __connman_connection_gateway_add(struct connman_service *service,
 {
        struct gateway_data *active_gateway = NULL;
        struct gateway_data *new_gateway = NULL;
+       enum connman_ipconfig_type type4 = CONNMAN_IPCONFIG_TYPE_UNKNOWN,
+               type6 = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
+       enum connman_service_type service_type =
+                                       connman_service_get_type(service);
        int index;
 
        index = __connman_service_get_index(service);
 
-       DBG("service %p index %d gateway %s vpn ip %s type %d",
-               service, index, gateway, peer, type);
-
        /*
         * If gateway is NULL, it's a point to point link and the default
         * gateway for ipv4 is 0.0.0.0 and for ipv6 is ::, meaning the
@@ -539,93 +848,39 @@ int __connman_connection_gateway_add(struct connman_service *service,
        if (gateway == NULL && type == CONNMAN_IPCONFIG_TYPE_IPV6)
                gateway = "::";
 
-       active_gateway = find_active_gateway();
+       DBG("service %p index %d gateway %s vpn ip %s type %d",
+               service, index, gateway, peer, type);
+
        new_gateway = add_gateway(service, index, gateway, type);
        if (new_gateway == NULL)
                return -EINVAL;
 
-       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->gateway,
-                                       NULL);
+       active_gateway = find_active_gateway();
 
-       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->gateway,
-                                       NULL);
+       DBG("active %p index %d new %p", active_gateway,
+               active_gateway ? active_gateway->index : -1, new_gateway);
 
        if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
                                new_gateway->ipv4_gateway != NULL) {
+               add_host_route(AF_INET, index, gateway, service_type);
                __connman_service_nameserver_add_routes(service,
                                        new_gateway->ipv4_gateway->gateway);
-               __connman_service_ipconfig_indicate_state(service,
-                                               CONNMAN_SERVICE_STATE_READY,
-                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+               type4 = CONNMAN_IPCONFIG_TYPE_IPV4;
        }
 
        if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
                                new_gateway->ipv6_gateway != NULL) {
+               add_host_route(AF_INET6, index, gateway, service_type);
                __connman_service_nameserver_add_routes(service,
                                        new_gateway->ipv6_gateway->gateway);
-               __connman_service_ipconfig_indicate_state(service,
-                                               CONNMAN_SERVICE_STATE_READY,
-                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+               type6 = CONNMAN_IPCONFIG_TYPE_IPV6;
        }
 
-       if (connman_service_get_type(service) == CONNMAN_SERVICE_TYPE_VPN) {
-               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) {
-                               const char *new_ipv4_gateway;
-
-                               new_ipv4_gateway =
-                                       active_gateway->ipv4_gateway->gateway;
-                               if (new_ipv4_gateway != NULL &&
-                                        g_strcmp0(new_ipv4_gateway,
-                                                       "0.0.0.0") != 0)
-                                       new_gateway->ipv4_gateway->vpn_phy_ip =
-                                               g_strdup(new_ipv4_gateway);
+       if (service_type == CONNMAN_SERVICE_TYPE_VPN) {
 
-                               new_gateway->ipv4_gateway->vpn_phy_index =
-                                                       active_gateway->index;
-                       }
+               set_vpn_routes(new_gateway, service, gateway, type, peer,
+                                                       active_gateway);
 
-               } 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) {
-                               const char *new_ipv6_gateway;
-
-                               new_ipv6_gateway =
-                                       active_gateway->ipv6_gateway->gateway;
-                               if (new_ipv6_gateway != NULL &&
-                                       g_strcmp0(new_ipv6_gateway, "::") != 0)
-                                       new_gateway->ipv6_gateway->vpn_phy_ip =
-                                               g_strdup(new_ipv6_gateway);
-
-                               new_gateway->ipv6_gateway->vpn_phy_index =
-                                                       active_gateway->index;
-                       }
-               }
        } else {
                if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
                                        new_gateway->ipv4_gateway != NULL)
@@ -638,29 +893,39 @@ int __connman_connection_gateway_add(struct connman_service *service,
 
        if (active_gateway == NULL) {
                set_default_gateway(new_gateway, type);
-               return 0;
+               goto done;
        }
 
        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->gateway,
-                                       active_gateway->ipv4_gateway->gateway);
-               connman_inet_clear_gateway_address(active_gateway->index,
+               if (__connman_service_is_split_routing(new_gateway->service) ==
+                                                                       FALSE)
+                       connman_inet_clear_gateway_address(
+                                       active_gateway->index,
                                        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,
+               if (__connman_service_is_split_routing(new_gateway->service) ==
+                                                                       FALSE)
+                       connman_inet_clear_ipv6_gateway_address(
+                                       active_gateway->index,
                                        active_gateway->ipv6_gateway->gateway);
        }
 
+done:
+       if (type4 == CONNMAN_IPCONFIG_TYPE_IPV4)
+               __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       if (type6 == CONNMAN_IPCONFIG_TYPE_IPV6)
+               __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
        return 0;
 }
 
@@ -681,7 +946,7 @@ void __connman_connection_gateway_remove(struct connman_service *service,
        else
                do_ipv4 = do_ipv6 = TRUE;
 
-       __connman_service_nameserver_del_routes(service);
+       __connman_service_nameserver_del_routes(service, type);
 
        data = g_hash_table_lookup(gateway_hash, service);
        if (data == NULL)
@@ -708,8 +973,6 @@ void __connman_connection_gateway_remove(struct connman_service *service,
                connman_inet_del_ipv6_host_route(data->index,
                                                data->ipv6_gateway->gateway);
 
-       __connman_service_nameserver_del_routes(service);
-
        err = disable_gateway(data, type);
 
        /*
@@ -743,45 +1006,86 @@ void __connman_connection_gateway_remove(struct connman_service *service,
 
 gboolean __connman_connection_update_gateway(void)
 {
-       struct gateway_data *active_gateway, *default_gateway;
+       struct gateway_data *default_gateway;
        gboolean updated = FALSE;
+       GHashTableIter iter;
+       gpointer value, key;
 
        if (gateway_hash == NULL)
                return updated;
 
-       active_gateway = find_active_gateway();
-
        update_order();
 
        default_gateway = find_default_gateway();
 
-       if (active_gateway && active_gateway != default_gateway) {
-               updated = TRUE;
+       __connman_service_update_ordering();
 
-               if (active_gateway->ipv4_gateway)
-                       unset_default_gateway(active_gateway,
-                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+       DBG("default %p", default_gateway);
 
-               if (active_gateway->ipv6_gateway)
-                       unset_default_gateway(active_gateway,
-                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+       /*
+        * There can be multiple active gateways so we need to
+        * check them all.
+        */
+       g_hash_table_iter_init(&iter, gateway_hash);
 
-               __connman_service_downgrade_state(active_gateway->service);
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *active_gateway = value;
+
+               if (active_gateway == default_gateway)
+                       continue;
 
-               if (default_gateway) {
-                       if (default_gateway->ipv4_gateway)
-                               set_default_gateway(default_gateway,
+               if (active_gateway->ipv4_gateway != NULL &&
+                               active_gateway->ipv4_gateway->active == TRUE) {
+
+                       unset_default_gateway(active_gateway,
                                                CONNMAN_IPCONFIG_TYPE_IPV4);
+                       updated = TRUE;
+               }
+
+               if (active_gateway->ipv6_gateway != NULL &&
+                               active_gateway->ipv6_gateway->active == TRUE) {
 
-                       if (default_gateway->ipv6_gateway)
-                               set_default_gateway(default_gateway,
+                       unset_default_gateway(active_gateway,
                                                CONNMAN_IPCONFIG_TYPE_IPV6);
+                       updated = TRUE;
                }
        }
 
+       if (updated && default_gateway != NULL) {
+               if (default_gateway->ipv4_gateway)
+                       set_default_gateway(default_gateway,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+
+               if (default_gateway->ipv6_gateway)
+                       set_default_gateway(default_gateway,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+       }
+
        return updated;
 }
 
+int __connman_connection_get_vpn_index(int phy_index)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               if (data->ipv4_gateway != NULL &&
+                               data->ipv4_gateway->vpn_phy_index == phy_index)
+                       return data->index;
+
+               if (data->ipv6_gateway != NULL &&
+                               data->ipv6_gateway->vpn_phy_index == phy_index)
+                       return data->index;
+       }
+
+       return -1;
+}
+
 int __connman_connection_init(void)
 {
        int err;