change data->gateway to data->ipv4_gateway in src/connection.c
[platform/upstream/connman.git] / src / connection.c
index 7344cd4..cb0b528 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2009  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2010  Intel Corporation. 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
 #include <config.h>
 #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>
 
 
 struct gateway_data {
        int index;
-       char *gateway;
+       char *ipv4_gateway;
+       struct connman_element *element;
+       unsigned int order;
+       gboolean active;
+       /* VPN extra data */
+       gboolean vpn;
+       char *vpn_ip;
+       int vpn_phy_index;
 };
 
 static GSList *gateway_list = NULL;
@@ -52,38 +54,82 @@ static struct gateway_data *find_gateway(int index, const char *gateway)
        for (list = gateway_list; list; list = list->next) {
                struct gateway_data *data = list->data;
 
-               if (data->gateway == NULL)
+               if (data->ipv4_gateway == NULL)
                        continue;
 
                if (data->index == index &&
-                               g_str_equal(data->gateway, gateway) == TRUE)
+                               g_str_equal(data->ipv4_gateway, gateway)
+                                                               == TRUE)
                        return data;
        }
 
        return NULL;
 }
 
-static void connection_newgateway(int index, const char *gateway)
+static int del_routes(struct gateway_data *data)
 {
-       struct gateway_data *data;
+       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_host_route(data->index, data->ipv4_gateway);
+               return connman_inet_clear_gateway_address(data->index,
+                                                       data->ipv4_gateway);
+       }
+}
 
-       DBG("index %d gateway %s", index, gateway);
+static void find_element(struct connman_element *element, gpointer user_data)
+{
+       struct gateway_data *data = user_data;
 
-       data = find_gateway(index, gateway);
-       if (data != NULL)
+       DBG("element %p name %s", element, element->name);
+
+       if (data->element != NULL)
                return;
 
+       if (element->index != data->index)
+               return;
+
+       data->element = element;
+}
+
+static struct gateway_data *add_gateway(int index, const char *gateway)
+{
+       struct gateway_data *data;
+       struct connman_service *service;
+
+       if (strlen(gateway) == 0)
+               return NULL;
+
        data = g_try_new0(struct gateway_data, 1);
        if (data == NULL)
-               return;
+               return NULL;
 
        data->index = index;
-       data->gateway = g_strdup(gateway);
+       data->ipv4_gateway = g_strdup(gateway);
+       data->active = FALSE;
+       data->element = NULL;
+       data->vpn_ip = NULL;
+       data->vpn = FALSE;
+       data->vpn_phy_index = -1;
+
+       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
+                                                       find_element, data);
+
+       service = __connman_element_get_service(data->element);
+       data->order = __connman_service_get_order(service);
 
        gateway_list = g_slist_append(gateway_list, data);
+
+       return data;
 }
 
-static void connection_delgateway(int index, const char *gateway)
+static void connection_newgateway(int index, const char *gateway)
 {
        struct gateway_data *data;
 
@@ -93,285 +139,208 @@ static void connection_delgateway(int index, const char *gateway)
        if (data == NULL)
                return;
 
-       gateway_list = g_slist_remove(gateway_list, data);
-
-       g_free(data->gateway);
-       g_free(data);
+       data->active = TRUE;
 }
 
-static struct connman_rtnl connection_rtnl = {
-       .name           = "connection",
-       .newgateway     = connection_newgateway,
-       .delgateway     = connection_delgateway,
-};
-
-static int set_route(struct connman_element *element, const char *gateway)
+static void set_default_gateway(struct gateway_data *data)
 {
-       struct ifreq ifr;
-       struct rtentry rt;
-       struct sockaddr_in *addr;
-       int sk, err;
-
-       DBG("element %p", element);
+       struct connman_element *element = data->element;
+       struct connman_service *service = NULL;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
-       if (sk < 0)
-               return -1;
+       DBG("gateway %s", data->ipv4_gateway);
 
-       memset(&ifr, 0, sizeof(ifr));
-       ifr.ifr_ifindex = element->index;
-
-       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
-               close(sk);
-               return -1;
+       if (data->vpn == TRUE) {
+               connman_inet_set_gateway_address(data->index, data->vpn_ip);
+               data->active = TRUE;
+               /* vpn gateway going away no changes in services */
+               return;
        }
 
-       DBG("ifname %s", ifr.ifr_name);
-
-       memset(&rt, 0, sizeof(rt));
-       rt.rt_flags = RTF_UP | RTF_GATEWAY;
-
-       addr = (struct sockaddr_in *) &rt.rt_dst;
-       addr->sin_family = AF_INET;
-       addr->sin_addr.s_addr = INADDR_ANY;
-
-       addr = (struct sockaddr_in *) &rt.rt_gateway;
-       addr->sin_family = AF_INET;
-       addr->sin_addr.s_addr = inet_addr(gateway);
-
-       addr = (struct sockaddr_in *) &rt.rt_genmask;
-       addr->sin_family = AF_INET;
-       addr->sin_addr.s_addr = INADDR_ANY;
-
-       err = ioctl(sk, SIOCADDRT, &rt);
-       if (err < 0)
-               DBG("default route setting failed (%s)", strerror(errno));
+       if (g_strcmp0(data->ipv4_gateway, "0.0.0.0") == 0) {
+               if (connman_inet_set_gateway_interface(element->index) < 0)
+                       return;
+               goto done;
+       }
 
-       close(sk);
+       if (connman_inet_set_gateway_address(element->index,
+                                       data->ipv4_gateway) < 0)
+               return;
 
-       return err;
+done:
+       service = __connman_element_get_service(element);
+       __connman_service_indicate_default(service);
 }
 
-static int del_route(struct connman_element *element, const char *gateway)
+static struct gateway_data *find_default_gateway(void)
 {
-       struct ifreq ifr;
-       struct rtentry rt;
-       struct sockaddr_in *addr;
-       int sk, err;
-
-       DBG("element %p", element);
-
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
-       if (sk < 0)
-               return -1;
+       struct gateway_data *found = NULL;
+       unsigned int order = 0;
+       GSList *list;
 
-       memset(&ifr, 0, sizeof(ifr));
-       ifr.ifr_ifindex = element->index;
+       for (list = gateway_list; list; list = list->next) {
+               struct gateway_data *data = list->data;
 
-       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
-               close(sk);
-               return -1;
+               if (found == NULL || data->order > order) {
+                       found = data;
+                       order = data->order;
+               }
        }
 
-       DBG("ifname %s", ifr.ifr_name);
-
-       memset(&rt, 0, sizeof(rt));
-       rt.rt_flags = RTF_UP | RTF_GATEWAY;
-
-       addr = (struct sockaddr_in *) &rt.rt_dst;
-       addr->sin_family = AF_INET;
-       addr->sin_addr.s_addr = INADDR_ANY;
-
-       addr = (struct sockaddr_in *) &rt.rt_gateway;
-       addr->sin_family = AF_INET;
-       addr->sin_addr.s_addr = inet_addr(gateway);
-
-       addr = (struct sockaddr_in *) &rt.rt_genmask;
-       addr->sin_family = AF_INET;
-       addr->sin_addr.s_addr = INADDR_ANY;
-
-       err = ioctl(sk, SIOCDELRT, &rt);
-       if (err < 0)
-               DBG("default route removal failed (%s)", strerror(errno));
-
-       close(sk);
-
-       return err;
+       return found;
 }
 
-static DBusMessage *get_properties(DBusConnection *conn,
-                                       DBusMessage *msg, void *data)
+static int remove_gateway(struct gateway_data *data)
 {
-       struct connman_element *element = data;
-       DBusMessage *reply;
-       DBusMessageIter array, dict;
-       const char *type = NULL, *method = NULL;
-       const char *address = NULL, *netmask = NULL, *gateway = NULL;
-
-       DBG("conn %p", conn);
-
-       reply = dbus_message_new_method_return(msg);
-       if (reply == NULL)
-               return NULL;
-
-       dbus_message_iter_init_append(reply, &array);
-
-       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);
-
-       connman_element_get_static_property(element, "Type", &type);
-
-       if (type != NULL)
-               connman_dbus_dict_append_variant(&dict, "Type",
-                                               DBUS_TYPE_STRING, &type);
-
-       if (element->devname != NULL)
-               connman_dbus_dict_append_variant(&dict, "Interface",
-                                       DBUS_TYPE_STRING, &element->devname);
-
-       connman_dbus_dict_append_variant(&dict, "Default",
-                                       DBUS_TYPE_BOOLEAN, &element->enabled);
-
-       connman_element_get_value(element,
-                               CONNMAN_PROPERTY_ID_IPV4_METHOD, &method);
-
-       connman_element_get_value(element,
-                               CONNMAN_PROPERTY_ID_IPV4_ADDRESS, &address);
-       connman_element_get_value(element,
-                               CONNMAN_PROPERTY_ID_IPV4_NETMASK, &netmask);
-       connman_element_get_value(element,
-                               CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
-
-       if (method != NULL)
-               connman_dbus_dict_append_variant(&dict, "IPv4.Method",
-                                               DBUS_TYPE_STRING, &method);
+       int err;
 
-       if (address != NULL)
-               connman_dbus_dict_append_variant(&dict, "IPv4.Address",
-                                               DBUS_TYPE_STRING, &address);
+       DBG("gateway %s", data->ipv4_gateway);
 
-       if (netmask != NULL)
-               connman_dbus_dict_append_variant(&dict, "IPv4.Netmask",
-                                               DBUS_TYPE_STRING, &netmask);
+       gateway_list = g_slist_remove(gateway_list, data);
 
-       if (gateway != NULL)
-               connman_dbus_dict_append_variant(&dict, "IPv4.Gateway",
-                                               DBUS_TYPE_STRING, &gateway);
+       if (data->active == TRUE)
+               err = del_routes(data);
+       else
+               err = 0;
 
-       dbus_message_iter_close_container(&array, &dict);
+       g_free(data->ipv4_gateway);
+       g_free(data->vpn_ip);
+       g_free(data);
 
-       return reply;
+       return err;
 }
 
-static DBusMessage *set_property(DBusConnection *conn,
-                                       DBusMessage *msg, void *data)
+static void connection_delgateway(int index, const char *gateway)
 {
-       DBusMessageIter iter, value;
-       const char *name;
-
-       DBG("conn %p", conn);
-
-       if (dbus_message_iter_init(msg, &iter) == FALSE)
-               return __connman_error_invalid_arguments(msg);
+       struct gateway_data *data;
 
-       dbus_message_iter_get_basic(&iter, &name);
-       dbus_message_iter_next(&iter);
-       dbus_message_iter_recurse(&iter, &value);
+       DBG("index %d gateway %s", index, gateway);
 
-       if (__connman_security_check_privileges(msg) < 0)
-               return __connman_error_permission_denied(msg);
+       data = find_gateway(index, gateway);
+       if (data != NULL)
+               data->active = FALSE;
 
-       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+       data = find_default_gateway();
+       if (data != NULL)
+               set_default_gateway(data);
 }
 
-static GDBusMethodTable connection_methods[] = {
-       { "GetProperties", "",   "a{sv}", get_properties },
-       { "SetProperty",   "sv", "",      set_property   },
-       { },
-};
-
-static GDBusSignalTable connection_signals[] = {
-       { "PropertyChanged", "sv" },
-       { },
+static struct connman_rtnl connection_rtnl = {
+       .name           = "connection",
+       .newgateway     = connection_newgateway,
+       .delgateway     = connection_delgateway,
 };
 
-static DBusConnection *connection;
-
-static void emit_connections_signal(void)
-{
-}
-
-static int register_interface(struct connman_element *element)
+static struct gateway_data *find_active_gateway(void)
 {
-       DBG("element %p name %s", element, element->name);
-
-       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;
-       }
-
-       emit_connections_signal();
+       GSList *list;
 
-       return 0;
-}
+       DBG("");
 
-static void unregister_interface(struct connman_element *element)
-{
-       DBG("element %p name %s", element, element->name);
+       for (list = gateway_list; list; list = list->next) {
+               struct gateway_data *data = list->data;
 
-       emit_connections_signal();
+               if (data->active == TRUE)
+                       return data;
+       }
 
-       g_dbus_unregister_interface(connection, element->path,
-                                               CONNMAN_CONNECTION_INTERFACE);
+       return NULL;
 }
 
 static int connection_probe(struct connman_element *element)
 {
+       struct connman_service *service = NULL;
        const char *gateway = NULL;
+       const char *vpn_ip = NULL;
+       struct gateway_data *active_gateway = NULL;
+       struct gateway_data *new_gateway = NULL;
 
        DBG("element %p name %s", element, element->name);
 
        if (element->parent == NULL)
                return -ENODEV;
 
-       if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
-               return -ENODEV;
+       /* FIXME: Remove temporarily for the static gateway support */
+       /* if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
+               return -ENODEV; */
 
        connman_element_get_value(element,
                                CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
 
+       connman_element_get_value(element,
+                                 CONNMAN_PROPERTY_ID_IPV4_ADDRESS, &vpn_ip);
+
        DBG("gateway %s", gateway);
 
-       if (register_interface(element) < 0)
-               return -ENODEV;
+       /*
+        * If gateway is NULL, it's a point to point link and the default
+        * gateway is 0.0.0.0, meaning the interface.
+        */
+       if (gateway == NULL) {
+               gateway = "0.0.0.0";
+               element->ipv4.gateway = g_strdup(gateway);
+       }
 
-       if (gateway == NULL)
+       connman_element_set_enabled(element, TRUE);
+
+       active_gateway = find_active_gateway();
+       new_gateway = add_gateway(element->index, gateway);
+       if (new_gateway == NULL)
                return 0;
 
-       if (g_slist_length(gateway_list) > 0) {
-               DBG("default gateway already present");
+       service = __connman_element_get_service(element);
+
+       connman_inet_add_host_route(element->index,
+                                       new_gateway->ipv4_gateway, NULL);
+       __connman_service_nameserver_add_routes(service,
+                                               new_gateway->ipv4_gateway);
+
+       __connman_service_indicate_state(service, CONNMAN_SERVICE_STATE_READY);
+
+       if (service == NULL) {
+               new_gateway->vpn = TRUE;
+               new_gateway->vpn_ip = g_strdup(vpn_ip);
+               /* make sure vpn gateway are at higher priority */
+               new_gateway->order = 10;
+               if (active_gateway)
+                       new_gateway->vpn_phy_index = active_gateway->index;
+       } else
+               new_gateway->vpn = FALSE;
+
+       if (active_gateway == NULL) {
+               set_default_gateway(new_gateway);
                return 0;
        }
 
-       set_route(element, gateway);
+       if (new_gateway->vpn == TRUE) {
+               connman_inet_add_host_route(active_gateway->index,
+                                               new_gateway->ipv4_gateway,
+                                               active_gateway->ipv4_gateway);
+       }
 
-       connman_element_set_enabled(element, TRUE);
+       if (new_gateway->order >= active_gateway->order) {
+               del_routes(active_gateway);
+               return 0;
+       }
 
        return 0;
 }
 
 static void connection_remove(struct connman_element *element)
 {
+       struct connman_service *service;
        const char *gateway = NULL;
+       struct gateway_data *data = NULL;
+       gboolean set_default = FALSE;
+       int err;
 
        DBG("element %p name %s", element, element->name);
 
-       unregister_interface(element);
+       service = __connman_element_get_service(element);
+       __connman_service_nameserver_del_routes(service);
+       __connman_service_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT);
+
+       connman_element_set_enabled(element, FALSE);
 
        connman_element_get_value(element,
                                CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
@@ -381,7 +350,27 @@ static void connection_remove(struct connman_element *element)
        if (gateway == NULL)
                return;
 
-       del_route(element, gateway);
+       data = find_gateway(element->index, gateway);
+       if (data == NULL)
+               return;
+
+       set_default = data->vpn;
+
+       if (data->vpn == TRUE && data->vpn_phy_index >= 0)
+               connman_inet_del_host_route(data->vpn_phy_index,
+                                               data->ipv4_gateway);
+       err = remove_gateway(data);
+
+       /* 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) {
+               data = find_default_gateway();
+               if (data != NULL)
+                       set_default_gateway(data);
+       }
 }
 
 static struct connman_driver connection_driver = {
@@ -396,13 +385,9 @@ int __connman_connection_init(void)
 {
        DBG("");
 
-       connection = connman_dbus_get_connection();
-
        if (connman_rtnl_register(&connection_rtnl) < 0)
                connman_error("Failed to setup RTNL gateway driver");
 
-       connman_rtnl_send_getroute();
-
        return connman_driver_register(&connection_driver);
 }
 
@@ -419,15 +404,48 @@ void __connman_connection_cleanup(void)
        for (list = gateway_list; list; list = list->next) {
                struct gateway_data *data = list->data;
 
-               DBG("index %d gateway %s", data->index, data->gateway);
+               DBG("index %d gateway %s", data->index, data->ipv4_gateway);
 
-               g_free(data->gateway);
+               g_free(data->ipv4_gateway);
                g_free(data);
                list->data = NULL;
        }
 
        g_slist_free(gateway_list);
        gateway_list = NULL;
+}
+
+static void update_order(void)
+{
+       GSList *list = NULL;
+
+       for (list = gateway_list; list; list = list->next) {
+               struct gateway_data *data = list->data;
+               struct connman_service *service;
+
+               /* vpn gataway is not attached to a service. */
+               if (data->vpn)
+                       continue;
+
+               service = __connman_element_get_service(data->element);
+               data->order = __connman_service_get_order(service);
+       }
+}
+
+gboolean __connman_connection_update_gateway(void)
+{
+       struct gateway_data *active_gateway, *default_gateway;
+       gboolean updated = FALSE;
+
+       update_order();
+
+       active_gateway = find_active_gateway();
+       default_gateway = find_default_gateway();
+
+       if (active_gateway && active_gateway != default_gateway) {
+               del_routes(active_gateway);
+               updated = TRUE;
+       }
 
-       dbus_connection_unref(connection);
+       return updated;
 }