technology: return already enabled when tethering is enabled
[framework/connectivity/connman.git] / src / connection.c
index 62d6a31..d40f428 100644 (file)
@@ -2,7 +2,8 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2009  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
  *  it under the terms of the GNU General Public License version 2 as
 #endif
 
 #include <errno.h>
-#include <unistd.h>
 #include <string.h>
-#include <sys/ioctl.h>
-#include <arpa/inet.h>
 #include <net/if.h>
-#include <net/route.h>
 
 #include <gdbus.h>
 
 #include "connman.h"
 
+struct gateway_config {
+       gboolean active;
+       char *gateway;
+
+       /* VPN extra data */
+       gboolean vpn;
+       char *vpn_ip;
+       int vpn_phy_index;
+       char *vpn_phy_ip;
+};
+
 struct gateway_data {
        int index;
-       char *gateway;
+       struct connman_service *service;
+       unsigned int order;
+       struct gateway_config *ipv4_gateway;
+       struct gateway_config *ipv6_gateway;
+       connman_bool_t default_checked;
 };
 
-static GSList *gateway_list = NULL;
+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)
 {
-       GSList *list;
+       GHashTableIter iter;
+       gpointer value, key;
 
        if (gateway == NULL)
                return NULL;
 
-       for (list = gateway_list; list; list = list->next) {
-               struct gateway_data *data = list->data;
+       g_hash_table_iter_init(&iter, gateway_hash);
 
-               if (data->gateway == NULL)
-                       continue;
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
 
-               if (data->index == index &&
-                               g_str_equal(data->gateway, gateway) == TRUE)
-                       return data;
+               if (data->ipv4_gateway != NULL && data->index == index &&
+                               g_str_equal(data->ipv4_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data->ipv4_gateway;
+
+               if (data->ipv6_gateway != NULL && data->index == index &&
+                               g_str_equal(data->ipv6_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data->ipv6_gateway;
        }
 
        return NULL;
 }
 
-static void remove_gateway(int index, const char *gateway)
+static struct gateway_data *lookup_gateway_data(struct gateway_config *config)
 {
-       struct gateway_data *data;
+       GHashTableIter iter;
+       gpointer value, key;
 
-       data = find_gateway(index, gateway);
-       if (data == NULL)
-               return;
+       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;
 
-       gateway_list = g_slist_remove(gateway_list, data);
+               if (data->ipv4_gateway != NULL &&
+                               data->ipv4_gateway == config)
+                       return data;
+
+               if (data->ipv6_gateway != NULL &&
+                               data->ipv6_gateway == config)
+                       return data;
+       }
+
+       return NULL;
 }
 
-static int set_route(struct connman_element *element, const char *gateway)
+/*
+ * Find the gateway that is serving the VPN link
+ */
+static struct gateway_data *find_phy_gateway(int index, const char *gateway)
 {
-       struct ifreq ifr;
-       struct rtentry rt;
-       struct sockaddr_in *addr;
-       int sk, err;
+       GHashTableIter iter;
+       gpointer value, key;
+
+       if (gateway == NULL)
+               return NULL;
 
-       DBG("element %p", element);
+       g_hash_table_iter_init(&iter, gateway_hash);
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
-       if (sk < 0)
-               return -1;
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
 
-       memset(&ifr, 0, sizeof(ifr));
-       ifr.ifr_ifindex = element->index;
+               if (data->ipv4_gateway != NULL && data->index != index &&
+                               g_str_equal(data->ipv4_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data;
 
-       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
-               close(sk);
-               return -1;
+               if (data->ipv6_gateway != NULL && data->index != index &&
+                               g_str_equal(data->ipv6_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data;
        }
 
-       DBG("ifname %s", ifr.ifr_name);
+       return NULL;
+}
 
-       memset(&rt, 0, sizeof(rt));
-       rt.rt_flags = RTF_UP | RTF_GATEWAY;
+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;
 
-       addr = (struct sockaddr_in *) &rt.rt_dst;
-       addr->sin_family = AF_INET;
-       addr->sin_addr.s_addr = INADDR_ANY;
+       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;
 
-       addr = (struct sockaddr_in *) &rt.rt_gateway;
-       addr->sin_family = AF_INET;
-       addr->sin_addr.s_addr = inet_addr(gateway);
+       if (config == NULL)
+               goto done;
 
-       addr = (struct sockaddr_in *) &rt.rt_genmask;
-       addr->sin_family = AF_INET;
-       addr->sin_addr.s_addr = INADDR_ANY;
+       config->vpn = TRUE;
+       if (peer != NULL)
+               config->vpn_ip = g_strdup(peer);
+       else if (gateway != NULL)
+               config->vpn_ip = g_strdup(gateway);
 
-       err = ioctl(sk, SIOCADDRT, &rt);
-       if (err < 0)
-               DBG("default route setting failed (%s)", strerror(errno));
+       index = __connman_ipconfig_get_index(ipconfig);
+       data = find_phy_gateway(index, gateway);
 
-       close(sk);
+       if (data == NULL)
+               goto done;
 
-       return err;
-}
+       /*
+        * 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;
 
-static int del_route(struct connman_element *element, const char *gateway)
-{
-       struct ifreq ifr;
-       struct rtentry rt;
-       struct sockaddr_in *addr;
-       int sk, err;
+       if (ipconfig != NULL) {
+               const char *address;
 
-       DBG("element %p", element);
+               address = __connman_ipconfig_get_local(ipconfig);
+               config->vpn_phy_ip = g_strdup(address);
+       }
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
-       if (sk < 0)
-               return -1;
+       config->vpn_phy_index = data->index;
 
-       memset(&ifr, 0, sizeof(ifr));
-       ifr.ifr_ifindex = element->index;
+       DBG("vpn %s phy %s index %d", config->vpn_ip,
+               config->vpn_phy_ip, config->vpn_phy_index);
 
-       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
-               close(sk);
-               return -1;
+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);
        }
+}
 
-       DBG("ifname %s", ifr.ifr_name);
+static int del_routes(struct gateway_data *data,
+                       enum connman_ipconfig_type type)
+{
+       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) {
+                       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);
+               }
+       }
 
-       memset(&rt, 0, sizeof(rt));
-       rt.rt_flags = RTF_UP | RTF_GATEWAY;
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL) {
+               if (data->ipv6_gateway->vpn == TRUE) {
+                       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);
+               }
+       }
 
-       addr = (struct sockaddr_in *) &rt.rt_dst;
-       addr->sin_family = AF_INET;
-       addr->sin_addr.s_addr = INADDR_ANY;
+       return (status4 < 0 ? status4 : status6);
+}
 
-       addr = (struct sockaddr_in *) &rt.rt_gateway;
-       addr->sin_family = AF_INET;
-       addr->sin_addr.s_addr = inet_addr(gateway);
+static int disable_gateway(struct gateway_data *data,
+                       enum connman_ipconfig_type type)
+{
+       gboolean active = FALSE;
 
-       addr = (struct sockaddr_in *) &rt.rt_genmask;
-       addr->sin_family = AF_INET;
-       addr->sin_addr.s_addr = INADDR_ANY;
+       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;
 
-       err = ioctl(sk, SIOCDELRT, &rt);
-       if (err < 0)
-               DBG("default route removal failed (%s)", strerror(errno));
+       DBG("type %d active %d", type, active);
 
-       close(sk);
+       if (active == TRUE)
+               return del_routes(data, type);
 
-       return err;
+       return 0;
 }
 
-static DBusConnection *connection;
-
-static void emit_default_signal(struct connman_element *element)
+static struct gateway_data *add_gateway(struct connman_service *service,
+                                       int index, const char *gateway,
+                                       enum connman_ipconfig_type type)
 {
-       DBusMessage *signal;
-       DBusMessageIter entry, value;
-       const char *key = "Default";
+       struct gateway_data *data, *old;
+       struct gateway_config *config;
 
-       signal = dbus_message_new_signal(element->path,
-                       CONNMAN_CONNECTION_INTERFACE, "PropertyChanged");
-       if (signal == NULL)
-               return;
+       if (gateway == NULL || strlen(gateway) == 0)
+               return NULL;
+
+       data = g_try_new0(struct gateway_data, 1);
+       if (data == NULL)
+               return NULL;
+
+       data->index = index;
+
+       config = g_try_new0(struct gateway_config, 1);
+       if (config == NULL) {
+               g_free(data);
+               return NULL;
+       }
 
-       dbus_message_iter_init_append(signal, &entry);
+       config->gateway = g_strdup(gateway);
+       config->vpn_ip = NULL;
+       config->vpn_phy_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;
+       }
 
-       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+       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;
+               }
+       } else {
+               /*
+                * Only take a ref if we are adding new stuff to hash.
+                */
+               connman_service_ref(service);
+       }
 
-       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
-                                       DBUS_TYPE_BOOLEAN_AS_STRING, &value);
-       dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN,
-                                                       &element->enabled);
-       dbus_message_iter_close_container(&entry, &value);
+       g_hash_table_replace(gateway_hash, service, data);
 
-       g_dbus_send_message(connection, signal);
+       return data;
 }
 
-static void set_default(struct connman_element *element, gpointer user_data)
+static void set_default_gateway(struct gateway_data *data,
+                               enum connman_ipconfig_type type)
 {
-       struct gateway_data *data = user_data;
+       int index;
+       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;
+
+       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->ipv4_gateway->vpn_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);
 
-       DBG("element %p name %s", element, element->name);
+               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);
+               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);
 
-       if (element->index != data->index)
                return;
+       }
 
-       if (element->enabled == TRUE)
+       index = __connman_service_get_index(data->service);
+
+       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;
+       }
+
+       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;
 
-       connman_element_set_enabled(element, TRUE);
-       emit_default_signal(element);
+done:
+       __connman_service_indicate_default(data->service);
 }
 
-static void del_default(struct connman_element *element, gpointer user_data)
+static void unset_default_gateway(struct gateway_data *data,
+                               enum connman_ipconfig_type type)
 {
-       struct gateway_data *data = user_data;
+       int index;
+       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;
+
+       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_del_host_route(data->index,
+                                               data->ipv4_gateway->vpn_ip);
+               connman_inet_clear_gateway_address(data->index,
+                                               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;
+       }
 
-       DBG("element %p name %s", element, element->name);
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
+                                       data->ipv6_gateway->vpn == TRUE) {
+               connman_inet_del_ipv6_host_route(data->index,
+                                               data->ipv6_gateway->vpn_ip);
+               connman_inet_clear_ipv6_gateway_address(data->index,
+                                               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);
 
-       if (element->index != data->index)
                return;
+       }
 
-       if (element->enabled == FALSE)
+       index = __connman_service_get_index(data->service);
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
+                       g_strcmp0(data->ipv4_gateway->gateway,
+                                                       "0.0.0.0") == 0) {
+               connman_inet_clear_gateway_interface(index);
                return;
+       }
 
-       connman_element_set_enabled(element, FALSE);
-       emit_default_signal(element);
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
+                       g_strcmp0(data->ipv6_gateway->gateway,
+                                                       "::") == 0) {
+               connman_inet_clear_ipv6_gateway_interface(index);
+               return;
+       }
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
+               connman_inet_clear_ipv6_gateway_address(index,
+                                               data->ipv6_gateway->gateway);
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
+               connman_inet_clear_gateway_address(index,
+                                               data->ipv4_gateway->gateway);
 }
 
-static void new_default(struct connman_element *element, gpointer user_data)
+static struct gateway_data *find_default_gateway(void)
 {
-       const char *gateway;
+       struct gateway_data *found = NULL;
+       unsigned int order = 0;
+       GHashTableIter iter;
+       gpointer value, key;
 
-       DBG("element %p name %s", element, element->name);
+       g_hash_table_iter_init(&iter, gateway_hash);
 
-       if (g_slist_length(gateway_list) > 0)
-               return;
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
 
-       connman_element_get_value(element,
-                               CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
+               if (found == NULL || data->order > order) {
+                       found = data;
+                       order = data->order;
 
-       DBG("gateway %s", gateway);
+                       DBG("default %p order %d", found, order);
+               }
+       }
 
-       if (gateway == NULL)
-               return;
+       return found;
+}
 
-       set_route(element, gateway);
+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;
+               }
+       }
 
-       connman_element_set_enabled(element, TRUE);
-       emit_default_signal(element);
+       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);
 
-       data = find_gateway(index, gateway);
-       if (data != NULL)
+       config = find_gateway(index, gateway);
+       if (config == NULL)
                return;
 
-       data = g_try_new0(struct gateway_data, 1);
+       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;
 
-       data->index = index;
-       data->gateway = g_strdup(gateway);
+       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);
 
-       gateway_list = g_slist_append(gateway_list, data);
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *candidate = value;
 
-       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
-                                                       set_default, data);
+               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 connection_delgateway(int index, const char *gateway)
+static void remove_gateway(gpointer user_data)
 {
-       struct gateway_data *data;
-
-       DBG("index %d gateway %s", index, gateway);
+       struct gateway_data *data = user_data;
 
-       data = find_gateway(index, gateway);
-       if (data == NULL)
-               return;
+       DBG("gateway ipv4 %p ipv6 %p", data->ipv4_gateway, data->ipv6_gateway);
 
-       gateway_list = g_slist_remove(gateway_list, data);
+       if (data->ipv4_gateway != NULL) {
+               g_free(data->ipv4_gateway->gateway);
+               g_free(data->ipv4_gateway->vpn_ip);
+               g_free(data->ipv4_gateway->vpn_phy_ip);
+               g_free(data->ipv4_gateway);
+       }
 
-       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
-                                                       del_default, data);
+       if (data->ipv6_gateway != NULL) {
+               g_free(data->ipv6_gateway->gateway);
+               g_free(data->ipv6_gateway->vpn_ip);
+               g_free(data->ipv6_gateway->vpn_phy_ip);
+               g_free(data->ipv6_gateway);
+       }
 
-       g_free(data->gateway);
        g_free(data);
+}
 
-       if (g_slist_length(gateway_list) > 0)
-               return;
+static void connection_delgateway(int index, const char *gateway)
+{
+       struct gateway_config *config;
+       struct gateway_data *data;
+
+       DBG("index %d gateway %s", index, gateway);
 
-       DBG("selecting new default gateway");
+       config = find_gateway(index, gateway);
+       if (config != NULL)
+               config->active = FALSE;
 
-       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
-                                                       new_default, NULL);
+       data = find_default_gateway();
+       if (data != NULL)
+               set_default_gateway(data, CONNMAN_IPCONFIG_TYPE_ALL);
 }
 
 static struct connman_rtnl connection_rtnl = {
@@ -305,276 +716,409 @@ static struct connman_rtnl connection_rtnl = {
        .delgateway     = connection_delgateway,
 };
 
-static DBusMessage *get_properties(DBusConnection *conn,
-                                       DBusMessage *msg, void *data)
+static struct gateway_data *find_active_gateway(void)
 {
-       struct connman_element *element = data;
-       DBusMessage *reply;
-       DBusMessageIter array, dict;
-       connman_uint8_t strength;
-       const char *device, *network;
-       const char *type;
+       GHashTableIter iter;
+       gpointer value, key;
 
-       DBG("conn %p", conn);
+       DBG("");
 
-       if (__connman_security_check_privilege(msg,
-                                       CONNMAN_SECURITY_PRIVILEGE_PUBLIC) < 0)
-               return __connman_error_permission_denied(msg);
+       g_hash_table_iter_init(&iter, gateway_hash);
 
-       reply = dbus_message_new_method_return(msg);
-       if (reply == NULL)
-               return NULL;
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
 
-       dbus_message_iter_init_append(reply, &array);
+               if (data->ipv4_gateway != NULL &&
+                               data->ipv4_gateway->active == TRUE)
+                       return data;
 
-       dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
-                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+               if (data->ipv6_gateway != NULL &&
+                               data->ipv6_gateway->active == TRUE)
+                       return data;
+       }
 
-       type = connman_element_get_string(element, "Type");
-       if (type != NULL)
-               connman_dbus_dict_append_variant(&dict, "Type",
-                                               DBUS_TYPE_STRING, &type);
+       return NULL;
+}
 
-       strength = connman_element_get_uint8(element, "Strength");
-       if (strength > 0)
-               connman_dbus_dict_append_variant(&dict, "Strength",
-                                               DBUS_TYPE_BYTE, &strength);
+static void update_order(void)
+{
+       GHashTableIter iter;
+       gpointer value, key;
 
-       if (element->devname != NULL)
-               connman_dbus_dict_append_variant(&dict, "Interface",
-                                       DBUS_TYPE_STRING, &element->devname);
+       DBG("");
 
-       connman_dbus_dict_append_variant(&dict, "Default",
-                                       DBUS_TYPE_BOOLEAN, &element->enabled);
+       g_hash_table_iter_init(&iter, gateway_hash);
 
-       device = __connman_element_get_device(element);
-       if (device != NULL)
-               connman_dbus_dict_append_variant(&dict, "Device",
-                                       DBUS_TYPE_OBJECT_PATH, &device);
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
 
-       network = __connman_element_get_network(element);
-       if (network != NULL)
-               connman_dbus_dict_append_variant(&dict, "Network",
-                                       DBUS_TYPE_OBJECT_PATH, &network);
+               data->order = __connman_service_get_order(data->service);
+       }
+}
+
+void __connman_connection_gateway_activate(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
+{
+       struct gateway_data *data = NULL;
 
-       __connman_element_append_ipv4(element, &dict);
+       data = g_hash_table_lookup(gateway_hash, service);
+       if (data == NULL)
+               return;
 
-       dbus_message_iter_close_container(&array, &dict);
+       DBG("gateway %p/%p type %d", data->ipv4_gateway,
+                                       data->ipv6_gateway, type);
 
-       return reply;
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               data->ipv4_gateway->active = TRUE;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               data->ipv6_gateway->active = TRUE;
 }
 
-static DBusMessage *set_property(DBusConnection *conn,
-                                       DBusMessage *msg, void *data)
+static void add_host_route(int family, int index, const char *gateway,
+                       enum connman_service_type service_type)
 {
-       DBusMessageIter iter, value;
-       const char *name;
-       int 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;
+       }
+}
 
-       DBG("conn %p", conn);
+int __connman_connection_gateway_add(struct connman_service *service,
+                                       const char *gateway,
+                                       enum connman_ipconfig_type type,
+                                       const char *peer)
+{
+       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;
 
-       if (dbus_message_iter_init(msg, &iter) == FALSE)
-               return __connman_error_invalid_arguments(msg);
+       index = __connman_service_get_index(service);
 
-       dbus_message_iter_get_basic(&iter, &name);
-       dbus_message_iter_next(&iter);
-       dbus_message_iter_recurse(&iter, &value);
+       /*
+        * 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
+        * interface
+        */
+       if (gateway == NULL && type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               gateway = "0.0.0.0";
 
-       if (__connman_security_check_privilege(msg,
-                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
-               return __connman_error_permission_denied(msg);
+       if (gateway == NULL && type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               gateway = "::";
 
-       type = dbus_message_iter_get_arg_type(&value);
+       DBG("service %p index %d gateway %s vpn ip %s type %d",
+               service, index, gateway, peer, type);
 
-       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-}
+       new_gateway = add_gateway(service, index, gateway, type);
+       if (new_gateway == NULL)
+               return -EINVAL;
 
-static GDBusMethodTable connection_methods[] = {
-       { "GetProperties", "",   "a{sv}", get_properties },
-       { "SetProperty",   "sv", "",      set_property   },
-       { },
-};
+       active_gateway = find_active_gateway();
 
-static GDBusSignalTable connection_signals[] = {
-       { "PropertyChanged", "sv" },
-       { },
-};
+       DBG("active %p index %d new %p", active_gateway,
+               active_gateway ? active_gateway->index : -1, new_gateway);
 
-static void append_connections(DBusMessageIter *entry)
-{
-       DBusMessageIter value, iter;
-       const char *key = "Connections";
+       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);
+               type4 = CONNMAN_IPCONFIG_TYPE_IPV4;
+       }
 
-       dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
+       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);
+               type6 = CONNMAN_IPCONFIG_TYPE_IPV6;
+       }
 
-       dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
-               DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
-                                                               &value);
+       if (service_type == CONNMAN_SERVICE_TYPE_VPN) {
 
-       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
-                               DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
-       __connman_element_list(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION, &iter);
-       dbus_message_iter_close_container(&value, &iter);
+               set_vpn_routes(new_gateway, service, gateway, type, peer,
+                                                       active_gateway);
 
-       dbus_message_iter_close_container(entry, &value);
-}
+       } else {
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                                       new_gateway->ipv4_gateway != NULL)
+                       new_gateway->ipv4_gateway->vpn = FALSE;
 
-static void emit_connections_signal(void)
-{
-       DBusMessage *signal;
-       DBusMessageIter entry;
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                                       new_gateway->ipv6_gateway != NULL)
+                       new_gateway->ipv6_gateway->vpn = FALSE;
+       }
 
-       signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
-                               CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
-       if (signal == NULL)
-               return;
+       if (active_gateway == NULL) {
+               set_default_gateway(new_gateway, type);
+               goto done;
+       }
 
-       dbus_message_iter_init_append(signal, &entry);
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                               new_gateway->ipv4_gateway != NULL &&
+                               new_gateway->ipv4_gateway->vpn == TRUE) {
+               if (__connman_service_is_split_routing(new_gateway->service) ==
+                                                                       FALSE)
+                       connman_inet_clear_gateway_address(
+                                       active_gateway->index,
+                                       active_gateway->ipv4_gateway->gateway);
+       }
 
-       append_connections(&entry);
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                               new_gateway->ipv6_gateway != NULL &&
+                               new_gateway->ipv6_gateway->vpn == TRUE) {
+               if (__connman_service_is_split_routing(new_gateway->service) ==
+                                                                       FALSE)
+                       connman_inet_clear_ipv6_gateway_address(
+                                       active_gateway->index,
+                                       active_gateway->ipv6_gateway->gateway);
+       }
 
-       g_dbus_send_message(connection, signal);
+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;
 }
 
-static int register_interface(struct connman_element *element)
+void __connman_connection_gateway_remove(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
 {
-       DBG("element %p name %s", element, element->name);
+       struct gateway_data *data = NULL;
+       gboolean set_default4 = FALSE, set_default6 = FALSE;
+       int do_ipv4 = FALSE, do_ipv6 = FALSE;
+       int err;
 
-       if (g_dbus_register_interface(connection, element->path,
-                                       CONNMAN_CONNECTION_INTERFACE,
-                                       connection_methods, connection_signals,
-                                       NULL, element, NULL) == FALSE) {
-               connman_error("Failed to register %s connection", element->path);
-               return -EIO;
-       }
+       DBG("service %p type %d", service, type);
 
-       emit_connections_signal();
+       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;
 
-       return 0;
+       __connman_service_nameserver_del_routes(service, type);
+
+       data = g_hash_table_lookup(gateway_hash, service);
+       if (data == NULL)
+               return;
+
+       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 (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->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);
+
+       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)
+               ) {
+               connman_service_unref(service);
+               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_default4 || set_default6 || err < 0) {
+               data = find_default_gateway();
+               if (data != NULL)
+                       set_default_gateway(data, type);
+       }
 }
 
-static void unregister_interface(struct connman_element *element)
+gboolean __connman_connection_update_gateway(void)
 {
-       DBG("element %p name %s", element, element->name);
+       struct gateway_data *default_gateway;
+       gboolean updated = FALSE;
+       GHashTableIter iter;
+       gpointer value, key;
 
-       emit_connections_signal();
+       if (gateway_hash == NULL)
+               return updated;
 
-       g_dbus_unregister_interface(connection, element->path,
-                                               CONNMAN_CONNECTION_INTERFACE);
-}
+       update_order();
 
-static int connection_probe(struct connman_element *element)
-{
-       const char *gateway = NULL;
+       default_gateway = find_default_gateway();
 
-       DBG("element %p name %s", element, element->name);
+       __connman_service_update_ordering();
 
-       if (element->parent == NULL)
-               return -ENODEV;
+       DBG("default %p", default_gateway);
 
-       if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
-               return -ENODEV;
+       /*
+        * There can be multiple active gateways so we need to
+        * check them all.
+        */
+       g_hash_table_iter_init(&iter, gateway_hash);
 
-       connman_element_get_value(element,
-                               CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *active_gateway = value;
 
-       DBG("gateway %s", gateway);
+               if (active_gateway == default_gateway)
+                       continue;
 
-       if (register_interface(element) < 0)
-               return -ENODEV;
+               if (active_gateway->ipv4_gateway != NULL &&
+                               active_gateway->ipv4_gateway->active == TRUE) {
 
-       if (gateway == NULL)
-               return 0;
+                       unset_default_gateway(active_gateway,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+                       updated = TRUE;
+               }
 
-       if (find_gateway(element->index, gateway) != NULL) {
-               DBG("previous gateway still present");
-               goto done;
-       }
+               if (active_gateway->ipv6_gateway != NULL &&
+                               active_gateway->ipv6_gateway->active == TRUE) {
 
-       if (g_slist_length(gateway_list) > 0) {
-               DBG("default gateway already present");
-               return 0;
+                       unset_default_gateway(active_gateway,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+                       updated = TRUE;
+               }
        }
 
-       set_route(element, gateway);
+       if (updated && default_gateway != NULL) {
+               if (default_gateway->ipv4_gateway)
+                       set_default_gateway(default_gateway,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
 
-done:
-       connman_element_set_enabled(element, TRUE);
-       emit_default_signal(element);
+               if (default_gateway->ipv6_gateway)
+                       set_default_gateway(default_gateway,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+       }
 
-       return 0;
+       return updated;
 }
 
-static void connection_remove(struct connman_element *element)
+int __connman_connection_get_vpn_index(int phy_index)
 {
-       const char *gateway = NULL;
-
-       DBG("element %p name %s", element, element->name);
-
-       unregister_interface(element);
-
-       connman_element_get_value(element,
-                               CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
+       GHashTableIter iter;
+       gpointer value, key;
 
-       DBG("gateway %s", gateway);
+       g_hash_table_iter_init(&iter, gateway_hash);
 
-       if (gateway == NULL)
-               return;
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
 
-       remove_gateway(element->index, gateway);
+               if (data->ipv4_gateway != NULL &&
+                               data->ipv4_gateway->vpn_phy_index == phy_index)
+                       return data->index;
 
-       connman_element_set_enabled(element, FALSE);
-       emit_default_signal(element);
+               if (data->ipv6_gateway != NULL &&
+                               data->ipv6_gateway->vpn_phy_index == phy_index)
+                       return data->index;
+       }
 
-       del_route(element, gateway);
+       return -1;
 }
 
-static struct connman_driver connection_driver = {
-       .name           = "connection",
-       .type           = CONNMAN_ELEMENT_TYPE_CONNECTION,
-       .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
-       .probe          = connection_probe,
-       .remove         = connection_remove,
-};
-
 int __connman_connection_init(void)
 {
+       int err;
+
        DBG("");
 
-       connection = connman_dbus_get_connection();
+       gateway_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, remove_gateway);
 
-       if (connman_rtnl_register(&connection_rtnl) < 0)
+       err = connman_rtnl_register(&connection_rtnl);
+       if (err < 0)
                connman_error("Failed to setup RTNL gateway driver");
 
-       connman_rtnl_send_getroute();
-
-       return connman_driver_register(&connection_driver);
+       return err;
 }
 
 void __connman_connection_cleanup(void)
 {
-       GSList *list;
+       GHashTableIter iter;
+       gpointer value, key;
 
        DBG("");
 
-       connman_driver_unregister(&connection_driver);
-
        connman_rtnl_unregister(&connection_rtnl);
 
-       for (list = gateway_list; list; list = list->next) {
-               struct gateway_data *data = list->data;
+       g_hash_table_iter_init(&iter, gateway_hash);
 
-               DBG("index %d gateway %s", data->index, data->gateway);
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
 
-               g_free(data->gateway);
-               g_free(data);
-               list->data = NULL;
+               disable_gateway(data, CONNMAN_IPCONFIG_TYPE_ALL);
        }
 
-       g_slist_free(gateway_list);
-       gateway_list = NULL;
-
-       dbus_connection_unref(connection);
+       g_hash_table_destroy(gateway_hash);
+       gateway_hash = NULL;
 }