Merge tag 'upstream/1.40' into tizen.
[platform/upstream/connman.git] / vpn / vpn-provider.c
index 6a6b661..8092b5d 100755 (executable)
@@ -44,7 +44,6 @@ static DBusConnection *connection;
 static GHashTable *provider_hash;
 static GSList *driver_list;
 static int configuration_count;
-static bool handle_routes;
 
 struct vpn_route {
        int family;
@@ -71,6 +70,7 @@ struct vpn_provider {
        char *host;
        char *domain;
        int family;
+       bool do_split_routing;
        GHashTable *routes;
        struct vpn_provider_driver *driver;
        void *driver_data;
@@ -91,6 +91,7 @@ struct vpn_provider {
        void *plugin_data;
        unsigned int auth_error_counter;
        unsigned int conn_error_counter;
+       unsigned int signal_watch;
 };
 
 static void append_properties(DBusMessageIter *iter,
@@ -366,22 +367,8 @@ static void set_user_networks(struct vpn_provider *provider, GSList *networks)
 static void del_routes(struct vpn_provider *provider)
 {
        GHashTableIter hash;
-       gpointer value, key;
 
        g_hash_table_iter_init(&hash, provider->user_routes);
-       while (handle_routes && g_hash_table_iter_next(&hash,
-                                               &key, &value)) {
-               struct vpn_route *route = value;
-               if (route->family == AF_INET6) {
-                       unsigned char prefixlen = atoi(route->netmask);
-                       connman_inet_del_ipv6_network_route(provider->index,
-                                                       route->network,
-                                                       prefixlen);
-               } else
-                       connman_inet_del_host_route(provider->index,
-                                               route->network);
-       }
-
        g_hash_table_remove_all(provider->user_routes);
        g_slist_free_full(provider->user_networks, free_route);
        provider->user_networks = NULL;
@@ -404,6 +391,16 @@ static void send_value(const char *path, const char *key, const char *value)
                                        &str);
 }
 
+static void send_value_boolean(const char *path, const char *key,
+                                                       dbus_bool_t value)
+{
+       connman_dbus_property_changed_basic(path,
+                                       VPN_CONNECTION_INTERFACE,
+                                       key,
+                                       DBUS_TYPE_BOOLEAN,
+                                       &value);
+}
+
 static gboolean provider_send_changed(gpointer data)
 {
        struct vpn_provider *provider = data;
@@ -474,6 +471,11 @@ static bool compare_network_lists(GSList *a, GSList *b)
        return true;
 }
 
+static const char *bool2str(bool value)
+{
+       return value ? "true" : "false";
+}
+
 static int set_provider_property(struct vpn_provider *provider,
                        const char *name, DBusMessageIter *value, int type)
 {
@@ -500,10 +502,17 @@ static int set_provider_property(struct vpn_provider *provider,
                del_routes(provider);
                provider->user_networks = networks;
                set_user_networks(provider, provider->user_networks);
+               send_routes(provider, provider->user_routes, "UserRoutes");
+       } else if (g_str_equal(name, "SplitRouting")) {
+               dbus_bool_t split_routing;
+
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return -EINVAL;
 
-               if (!handle_routes)
-                       send_routes(provider, provider->user_routes,
-                                               "UserRoutes");
+               dbus_message_iter_get_basic(value, &split_routing);
+
+               DBG("property %s value %s ", name, bool2str(split_routing));
+               vpn_provider_set_boolean(provider, name, split_routing, false);
        } else {
                const char *str;
 
@@ -576,8 +585,13 @@ static DBusMessage *set_properties(DBusMessageIter *iter, DBusMessage *msg,
                dbus_message_iter_recurse(&entry, &value);
 
                type = dbus_message_iter_get_arg_type(&value);
-               /* Ignore and report back all invalid property types */
-               if (type != DBUS_TYPE_STRING && type != DBUS_TYPE_ARRAY) {
+               switch (type) {
+               case DBUS_TYPE_STRING:
+               case DBUS_TYPE_ARRAY:
+               case DBUS_TYPE_BOOLEAN:
+                       break;
+               default:
+                       /* Ignore and report back all invalid property types */
                        invalid = append_to_gstring(invalid, key);
                        continue;
                }
@@ -702,8 +716,7 @@ static DBusMessage *clear_property(DBusConnection *conn, DBusMessage *msg,
 
                del_routes(provider);
 
-               if (!handle_routes)
-                       send_routes(provider, provider->user_routes, name);
+               send_routes(provider, provider->user_routes, name);
        } else if (vpn_provider_get_string(provider, name)) {
                err = vpn_provider_set_string(provider, name, NULL);
                switch (err) {
@@ -841,6 +854,8 @@ static void provider_resolv_host_addr(struct vpn_provider *provider)
 void __vpn_provider_append_properties(struct vpn_provider *provider,
                                                        DBusMessageIter *iter)
 {
+       dbus_bool_t split_routing;
+
        if (provider->host)
                connman_dbus_dict_append_basic(iter, "Host",
                                        DBUS_TYPE_STRING, &provider->host);
@@ -852,6 +867,10 @@ void __vpn_provider_append_properties(struct vpn_provider *provider,
        if (provider->type)
                connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
                                                 &provider->type);
+
+       split_routing = provider->do_split_routing;
+       connman_dbus_dict_append_basic(iter, "SplitRouting", DBUS_TYPE_BOOLEAN,
+                                                       &split_routing);
 }
 
 int __vpn_provider_append_user_route(struct vpn_provider *provider,
@@ -984,7 +1003,7 @@ static GSList *get_routes(gchar **networks)
 static int provider_load_from_keyfile(struct vpn_provider *provider,
                GKeyFile *keyfile)
 {
-       gsize idx = 0;
+       gsize idx;
        gchar **settings;
        gchar *key, *value;
        gsize length, num_user_networks;
@@ -997,28 +1016,26 @@ static int provider_load_from_keyfile(struct vpn_provider *provider,
                return -ENOENT;
        }
 
-       while (idx < length) {
+       for (idx = 0; idx < length; idx++) {
                key = settings[idx];
-               if (key) {
-                       if (g_str_equal(key, "Networks")) {
-                               networks = __vpn_config_get_string_list(keyfile,
-                                               provider->identifier,
-                                               key,
-                                               &num_user_networks,
+               if (!key)
+                       continue;
+
+               if (g_str_equal(key, "Networks")) {
+                       networks = __vpn_config_get_string_list(keyfile,
+                                               provider->identifier,key,
+                                               &num_user_networks, NULL);
+                       provider->user_networks = get_routes(networks);
+               } else {
+                       value = __vpn_config_get_string(keyfile,
+                                               provider->identifier, key,
                                                NULL);
-                               provider->user_networks = get_routes(networks);
-
-                       } else {
-                               value = __vpn_config_get_string(keyfile,
-                                                       provider->identifier,
-                                                       key, NULL);
-                               vpn_provider_set_string(provider, key,
-                                                       value);
-                               g_free(value);
-                       }
+
+                       vpn_provider_set_string(provider, key, value);
+                       g_free(value);
                }
-               idx += 1;
        }
+
        g_strfreev(settings);
        g_strfreev(networks);
 
@@ -1135,6 +1152,7 @@ static int vpn_provider_save(struct vpn_provider *provider)
                        "Host", provider->host);
        g_key_file_set_string(keyfile, provider->identifier,
                        "VPN.Domain", provider->domain);
+
        if (provider->user_networks) {
                gchar **networks;
                gsize network_count;
@@ -1725,6 +1743,7 @@ static void append_properties(DBusMessageIter *iter,
        GHashTableIter hash;
        gpointer value, key;
        dbus_bool_t immutable;
+       dbus_bool_t split_routing;
 
        connman_dbus_dict_open(iter, &dict);
 
@@ -1752,6 +1771,10 @@ static void append_properties(DBusMessageIter *iter,
        connman_dbus_dict_append_basic(&dict, "Immutable", DBUS_TYPE_BOOLEAN,
                                        &immutable);
 
+       split_routing = provider->do_split_routing;
+       connman_dbus_dict_append_basic(&dict, "SplitRouting",
+                                       DBUS_TYPE_BOOLEAN, &split_routing);
+
        if (provider->family == AF_INET)
                connman_dbus_dict_append_dict(&dict, "IPv4", append_ipv4,
                                                provider);
@@ -1776,8 +1799,7 @@ static void append_properties(DBusMessageIter *iter,
                while (g_hash_table_iter_next(&hash, &key, &value)) {
                        struct vpn_setting *setting = value;
 
-                       if (!setting->hide_value &&
-                                                       setting->value)
+                       if (!setting->hide_value && setting->value)
                                connman_dbus_dict_append_basic(&dict, key,
                                                        DBUS_TYPE_STRING,
                                                        &setting->value);
@@ -1806,55 +1828,6 @@ static void connection_added_signal(struct vpn_provider *provider)
        dbus_message_unref(signal);
 }
 
-static bool check_host(char **hosts, char *host)
-{
-       int i;
-
-       if (!hosts)
-               return false;
-
-       for (i = 0; hosts[i]; i++) {
-               if (g_strcmp0(hosts[i], host) == 0)
-                       return true;
-       }
-
-       return false;
-}
-
-static void provider_append_routes(gpointer key, gpointer value,
-                                       gpointer user_data)
-{
-       struct vpn_route *route = value;
-       struct vpn_provider *provider = user_data;
-       int index = provider->index;
-
-       if (!handle_routes)
-               return;
-
-       /*
-        * If the VPN administrator/user has given a route to
-        * VPN server, then we must discard that because the
-        * server cannot be contacted via VPN tunnel.
-        */
-       if (check_host(provider->host_ip, route->network)) {
-               DBG("Discarding VPN route to %s via %s at index %d",
-                       route->network, route->gateway, index);
-               return;
-       }
-
-       if (route->family == AF_INET6) {
-               unsigned char prefix_len = atoi(route->netmask);
-
-               connman_inet_add_ipv6_network_route(index, route->network,
-                                                       route->gateway,
-                                                       prefix_len);
-       } else {
-               connman_inet_add_network_route(index, route->network,
-                                               route->gateway,
-                                               route->netmask);
-       }
-}
-
 static int set_connected(struct vpn_provider *provider,
                                        bool connected)
 {
@@ -1871,18 +1844,8 @@ static int set_connected(struct vpn_provider *provider,
 
                __vpn_ipconfig_address_add(ipconfig, provider->family);
 
-               if (handle_routes)
-                       __vpn_ipconfig_gateway_add(ipconfig, provider->family);
-
                provider_indicate_state(provider,
                                        VPN_PROVIDER_STATE_READY);
-
-               g_hash_table_foreach(provider->routes, provider_append_routes,
-                                       provider);
-
-               g_hash_table_foreach(provider->user_routes,
-                                       provider_append_routes, provider);
-
        } else {
                provider_indicate_state(provider,
                                        VPN_PROVIDER_STATE_DISCONNECT);
@@ -1949,10 +1912,61 @@ int vpn_provider_indicate_error(struct vpn_provider *provider,
        return 0;
 }
 
+static gboolean provider_property_changed(DBusConnection *conn,
+                                       DBusMessage *message, void *user_data)
+{
+       DBusMessageIter iter;
+       DBusMessageIter value;
+       struct vpn_provider *provider = user_data;
+       const char *key;
+
+       if (!dbus_message_iter_init(message, &iter))
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       DBG("provider %p key %s", provider, key);
+
+       if (g_str_equal(key, "SplitRouting")) {
+               dbus_bool_t split_routing;
+
+               if (dbus_message_iter_get_arg_type(&value) !=
+                                                       DBUS_TYPE_BOOLEAN)
+                       goto out;
+
+               dbus_message_iter_get_basic(&value, &split_routing);
+
+               DBG("property %s value %s", key, bool2str(split_routing));
+
+               /*
+                * Even though this is coming from connmand, signal the value
+                * for other components listening to the changes via VPN API
+                * only. provider.c will skip setting the same value in order
+                * to avoid signaling loop. This is needed for ensuring that
+                * all components using VPN API will be informed about the
+                * correct status of SplitRouting. Especially when loading the
+                * services after a crash, for instance.
+                */
+               vpn_provider_set_boolean(provider, "SplitRouting",
+                                       split_routing, true);
+       }
+
+out:
+       return TRUE;
+}
+
 static int connection_unregister(struct vpn_provider *provider)
 {
        DBG("provider %p path %s", provider, provider->path);
 
+       if (provider->signal_watch) {
+               g_dbus_remove_watch(connection, provider->signal_watch);
+               provider->signal_watch = 0;
+       }
+
        if (!provider->path)
                return -EALREADY;
 
@@ -1967,6 +1981,8 @@ static int connection_unregister(struct vpn_provider *provider)
 
 static int connection_register(struct vpn_provider *provider)
 {
+       char *connmand_vpn_path;
+
        DBG("provider %p path %s", provider, provider->path);
 
        if (provider->path)
@@ -1980,6 +1996,18 @@ static int connection_register(struct vpn_provider *provider)
                                connection_methods, connection_signals,
                                NULL, provider, NULL);
 
+       connmand_vpn_path = g_strdup_printf("%s/service/vpn_%s", CONNMAN_PATH,
+                                               provider->identifier);
+
+       provider->signal_watch = g_dbus_add_signal_watch(connection,
+                                       CONNMAN_SERVICE, connmand_vpn_path,
+                                       CONNMAN_SERVICE_INTERFACE,
+                                       PROPERTY_CHANGED,
+                                       provider_property_changed,
+                                       provider, NULL);
+
+       g_free(connmand_vpn_path);
+
        return 0;
 }
 
@@ -2017,6 +2045,7 @@ static void provider_initialize(struct vpn_provider *provider)
        provider->domain = NULL;
        provider->identifier = NULL;
        provider->immutable = false;
+       provider->do_split_routing = false;
        provider->user_networks = NULL;
        provider->routes = g_hash_table_new_full(g_direct_hash, g_direct_equal,
                                        NULL, free_route);
@@ -2112,9 +2141,12 @@ static struct vpn_provider *provider_create_from_keyfile(GKeyFile *keyfile,
                        return NULL;
                }
 
-               if (provider_register(provider) == 0)
+               if (!provider_register(provider)) {
                        connection_register(provider);
+                       connection_added_signal(provider);
+               }
        }
+
        return provider;
 }
 
@@ -2203,6 +2235,7 @@ int __vpn_provider_create(DBusMessage *msg)
        GSList *networks = NULL;
        char *ident;
        int err;
+       dbus_bool_t split_routing = false;
 
        dbus_message_iter_init(msg, &iter);
        dbus_message_iter_recurse(&iter, &array);
@@ -2229,6 +2262,11 @@ int __vpn_provider_create(DBusMessage *msg)
                                        g_str_equal(key, "Domain"))
                                dbus_message_iter_get_basic(&value, &domain);
                        break;
+               case DBUS_TYPE_BOOLEAN:
+                       if (g_str_equal(key, "SplitRouting"))
+                               dbus_message_iter_get_basic(&value,
+                                                       &split_routing);
+                       break;
                case DBUS_TYPE_ARRAY:
                        if (g_str_equal(key, "UserRoutes"))
                                networks = get_user_networks(&value);
@@ -2266,6 +2304,7 @@ int __vpn_provider_create(DBusMessage *msg)
                provider->domain = g_strdup(domain);
                provider->name = g_strdup(name);
                provider->type = g_strdup(type);
+               provider->do_split_routing = split_routing;
 
                if (provider_register(provider) == 0)
                        vpn_provider_load(provider);
@@ -2594,6 +2633,10 @@ static int set_string(struct vpn_provider *provider,
                g_free(provider->domain);
                provider->domain = g_strdup(value);
                send_value(provider->path, "Domain", provider->domain);
+       } else if (g_str_equal(key, "SplitRouting")) {
+               connman_warn("VPN SplitRouting value attempted to set as "
+                                       "string, is boolean");
+               return -EINVAL;
        } else {
                struct vpn_setting *setting;
                bool replace = true;
@@ -2680,6 +2723,25 @@ const char *vpn_provider_get_string(struct vpn_provider *provider,
        return setting->value;
 }
 
+int vpn_provider_set_boolean(struct vpn_provider *provider, const char *key,
+                                               bool value, bool force_change)
+{
+       DBG("provider %p key %s", provider, key);
+
+       if (g_str_equal(key, "SplitRouting")) {
+               if (provider->do_split_routing == value && !force_change)
+                       return -EALREADY;
+
+               DBG("SplitRouting set to %s", bool2str(value));
+
+               provider->do_split_routing = value;
+               send_value_boolean(provider->path, key,
+                                       provider->do_split_routing);
+       }
+
+       return 0;
+}
+
 bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key,
                                                        bool default_value)
 {
@@ -2952,11 +3014,8 @@ int vpn_provider_append_route(struct vpn_provider *provider,
                break;
        }
 
-       if (!handle_routes) {
-               if (route->netmask && route->gateway &&
-                                                       route->network)
-                       provider_schedule_changed(provider);
-       }
+       if (route->netmask && route->gateway && route->network)
+               provider_schedule_changed(provider);
 
        return 0;
 }
@@ -3015,6 +3074,12 @@ void vpn_provider_driver_unregister(struct vpn_provider_driver *driver)
                if (provider && provider->driver &&
                                g_strcmp0(provider->driver->name,
                                                        driver->name) == 0) {
+                       /*
+                        * Cancel VPN agent request to avoid segfault at
+                        * shutdown as the callback, if set can point to a
+                        * function in the plugin that is to be removed.
+                        */
+                       connman_agent_cancel(provider);
                        provider->driver = NULL;
                }
        }
@@ -3093,7 +3158,7 @@ void vpn_provider_clear_address(struct vpn_provider *provider, int family)
                        DBG("ipv6 %s/%d", address, len);
 
                        connman_inet_clear_ipv6_address(provider->index,
-                                                       address, len);
+                                               provider->prev_ipv6_addr);
 
                        connman_ipaddress_free(provider->prev_ipv6_addr);
                        provider->prev_ipv6_addr = NULL;
@@ -3183,14 +3248,12 @@ static void remove_unprovisioned_providers(void)
        g_strfreev(providers);
 }
 
-int __vpn_provider_init(bool do_routes)
+int __vpn_provider_init(void)
 {
        int err;
 
        DBG("");
 
-       handle_routes = do_routes;
-
        err = connman_agent_driver_register(&agent_driver);
        if (err < 0) {
                connman_error("Cannot register agent driver for %s",