diff options
Diffstat (limited to 'vpn/vpn-provider.c')
-rwxr-xr-x | vpn/vpn-provider.c | 287 |
1 files changed, 175 insertions, 112 deletions
diff --git a/vpn/vpn-provider.c b/vpn/vpn-provider.c index 6a6b6617..8092b5d6 100755 --- a/vpn/vpn-provider.c +++ b/vpn/vpn-provider.c @@ -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", |