--- include/provider.h | 10 ++ plugins/vpn.c | 292 +++++++++++++++++++++++++++++++++++++++++++- src/provider.c | 42 +------ vpn/vpn-provider.c | 352 ++++++++++++++++++++++++++++++++++++++--------------- 4 files changed, 556 insertions(+), 140 deletions(-)
diff --git a/include/provider.h b/include/provider.h index b663f37..548bd61 100644 --- a/include/provider.h +++ b/include/provider.h @@ -56,6 +56,13 @@ enum connman_provider_error { CONNMAN_PROVIDER_ERROR_AUTH_FAILED = 3, }; +enum connman_provider_route_type { + CONNMAN_PROVIDER_ROUTE_UNKNOWN = 0, + CONNMAN_PROVIDER_ROUTE_ALL = 0, + CONNMAN_PROVIDER_ROUTE_USER = 1, + CONNMAN_PROVIDER_ROUTE_SERVER = 2, +}; + struct connman_provider; struct connman_ipaddress; @@ -119,6 +126,9 @@ struct connman_provider_driver { const char * (*get_property) (struct connman_provider *provider, const char *key); int (*create) (DBusMessage *msg); + int (*set_routes) (struct connman_provider *provider, + enum connman_provider_route_type type); + connman_bool_t (*check_routes) (struct connman_provider *provider); }; int connman_provider_driver_register(struct connman_provider_driver *driver); diff --git a/plugins/vpn.c b/plugins/vpn.c index 5059952..aff821b 100644 --- a/plugins/vpn.c +++ b/plugins/vpn.c @@ -39,6 +39,7 @@ #include <connman/provider.h> #include <connman/ipaddress.h> #include <connman/vpn-dbus.h> +#include <connman/inet.h> #define DBUS_TIMEOUT 10000 @@ -51,6 +52,13 @@ static guint added_watch; static guint removed_watch; static guint property_watch; +struct vpn_route { + int family; + char *host; + char *netmask; + char *gateway; +}; + struct connection_data { char *path; struct connman_provider *provider; @@ -64,6 +72,8 @@ struct connection_data { char *domain; char **nameservers; + GHashTable *server_routes; + GHashTable *user_routes; GHashTable *setting_strings; struct connman_ipaddress *ip; @@ -197,6 +207,16 @@ out: return err; } +static void destroy_route(gpointer user_data) +{ + struct vpn_route *route = user_data; + + g_free(route->host); + g_free(route->netmask); + g_free(route->gateway); + g_free(route); +} + static struct connection_data *create_connection_data(const char *path) { struct connection_data *data; @@ -213,6 +233,11 @@ static struct connection_data *create_connection_data(const char *path) data->setting_strings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + data->server_routes = g_hash_table_new_full(g_direct_hash, + g_str_equal, g_free, destroy_route); + data->user_routes = g_hash_table_new_full(g_str_hash, + g_str_equal, g_free, destroy_route); + return data; } @@ -887,6 +912,74 @@ done: return err; } +static void set_route(struct connection_data *data, struct vpn_route *route) +{ + if (route->family == AF_INET6) { + unsigned char prefix_len = atoi(route->netmask); + + connman_inet_add_ipv6_network_route(data->index, route->host, + route->gateway, + prefix_len); + } else { + connman_inet_add_network_route(data->index, route->host, + route->gateway, + route->netmask); + } +} + +static int set_routes(struct connman_provider *provider, + enum connman_provider_route_type type) +{ + struct connection_data *data; + GHashTableIter iter; + gpointer value, key; + + DBG("provider %p", provider); + + data = connman_provider_get_data(provider); + if (data == NULL) + return -EINVAL; + + if (type == CONNMAN_PROVIDER_ROUTE_ALL || + type == CONNMAN_PROVIDER_ROUTE_USER) { + g_hash_table_iter_init(&iter, data->user_routes); + + while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) + set_route(data, value); + } + + if (type == CONNMAN_PROVIDER_ROUTE_ALL || + type == CONNMAN_PROVIDER_ROUTE_SERVER) { + g_hash_table_iter_init(&iter, data->server_routes); + + while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) + set_route(data, value); + } + + return 0; +} + +static connman_bool_t check_routes(struct connman_provider *provider) +{ + struct connection_data *data; + + DBG("provider %p", provider); + + data = connman_provider_get_data(provider); + if (data == NULL) + return FALSE; + + if (data->user_routes != NULL && + g_hash_table_size(data->user_routes) > 0) + return TRUE; + + if (data->server_routes != NULL && + g_hash_table_size(data->server_routes) > 0) + return TRUE; + + return FALSE; +} + static struct connman_provider_driver provider_driver = { .name = "VPN", .type = CONNMAN_PROVIDER_TYPE_VPN, @@ -897,6 +990,8 @@ static struct connman_provider_driver provider_driver = { .set_property = set_string, .get_property = get_string, .create = create_configuration, + .set_routes = set_routes, + .check_routes = check_routes, }; static void destroy_provider(struct connection_data *data) @@ -930,6 +1025,8 @@ static void connection_destroy(gpointer hash_data) g_free(data->name); g_free(data->host); g_free(data->domain); + g_hash_table_destroy(data->server_routes); + g_hash_table_destroy(data->user_routes); g_strfreev(data->nameservers); g_hash_table_destroy(data->setting_strings); @@ -1018,6 +1115,195 @@ static gboolean connection_added(DBusConnection *conn, DBusMessage *message, return TRUE; } +static int save_route(GHashTable *routes, int family, const char *network, + const char *netmask, const char *gateway) +{ + struct vpn_route *route; + char *key = g_strdup_printf("%d/%s/%s", family, network, netmask); + + DBG("family %d network %s netmask %s", family, network, netmask); + + route = g_hash_table_lookup(routes, key); + if (route == NULL) { + route = g_try_new0(struct vpn_route, 1); + if (route == NULL) { + connman_error("out of memory"); + return -ENOMEM; + } + + route->family = family; + route->host = g_strdup(network); + route->netmask = g_strdup(netmask); + route->gateway = g_strdup(gateway); + + g_hash_table_replace(routes, key, route); + } else + g_free(key); + + return 0; +} + +static void server_route_changed(DBusMessageIter *array, + struct connection_data *data) +{ + DBusMessageIter dict; + int family = AF_INET; + char *host = NULL, *netmask = NULL, *gateway = NULL; + + if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) { + DBG("Expecting array, ignoring routes."); + return; + } + + dbus_message_iter_recurse(array, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *key; + + dbus_message_iter_recurse(&dict, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + if (g_str_equal(key, "ProtocolFamily") == TRUE) { + int pf; + dbus_message_iter_get_basic(&value, &pf); + switch (pf) { + case 4: + family = AF_INET; + break; + case 6: + family = AF_INET6; + break; + } + DBG("family %d", family); + } else if (g_str_equal(key, "Netmask") == TRUE) { + dbus_message_iter_get_basic(&value, &netmask); + DBG("netmask %s", netmask); + } else if (g_str_equal(key, "Host") == TRUE) { + dbus_message_iter_get_basic(&value, &host); + DBG("host %s", host); + } else if (g_str_equal(key, "Gateway") == TRUE) { + dbus_message_iter_get_basic(&value, &gateway); + DBG("gateway %s", gateway); + } + + dbus_message_iter_next(&dict); + } + + if (netmask == NULL || host == NULL || gateway == NULL) { + DBG("Value missing."); + return; + } + + save_route(data->server_routes, family, host, netmask, gateway); +} + +static int parse_user_route(const char *route, int *user_family, + char **user_network, char **user_netmask) +{ + char *network, *netmask; + char **elems; + int family = PF_UNSPEC; + + if (route == NULL) + return -EINVAL; + + elems = g_strsplit(route, "/", 0); + if (elems == NULL) + return -EINVAL; + + network = elems[0]; + if (network == NULL || *network == '\0') { + DBG("no network/netmask set"); + g_strfreev(elems); + return -EINVAL; + } + + netmask = elems[1]; + if (netmask != NULL && *netmask == '\0') { + DBG("no netmask set"); + g_strfreev(elems); + return -EINVAL; + } + + if (g_strrstr(network, ":") != NULL) + family = AF_INET6; + else if (g_strrstr(network, ".") != NULL) { + family = AF_INET; + + if (g_strrstr(netmask, ".") == NULL) { + /* We have netmask length */ + in_addr_t addr; + struct in_addr netmask_in; + unsigned char prefix_len = 32; + + if (netmask != NULL) + prefix_len = atoi(netmask); + + addr = 0xffffffff << (32 - prefix_len); + netmask_in.s_addr = htonl(addr); + netmask = inet_ntoa(netmask_in); + + DBG("network %s netmask %s", network, netmask); + } + } + + if (user_family != NULL) + *user_family = family; + + if (user_network != NULL) + *user_network = network; + + if (user_netmask != NULL) + *user_netmask = netmask; + + g_strfreev(elems); + + return 0; +} + +static void add_user_route(GHashTable *user_routes, const char *route) +{ + char *network, *netmask; + int family, err; + + err = parse_user_route(route, &family, &network, &netmask); + if (err < 0) + return; + + save_route(user_routes, family, network, netmask, NULL); +} + +static int user_routes_changed(DBusMessageIter *array, + struct connection_data *data) +{ + GHashTable *user_routes; + DBusMessageIter entry; + + dbus_message_iter_recurse(array, &entry); + + user_routes = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, destroy_route); + + while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { + const char *route; + + dbus_message_iter_get_basic(&entry, &route); + + add_user_route(user_routes, route); + + dbus_message_iter_next(&entry); + } + + g_hash_table_destroy(data->user_routes); + data->user_routes = user_routes; + + return 0; +} + static gboolean property_changed(DBusConnection *conn, DBusMessage *message, void *user_data) @@ -1074,10 +1360,10 @@ static gboolean property_changed(DBusConnection *conn, } else if (g_str_equal(key, "IPv6") == TRUE) { err = extract_ip(&value, AF_INET6, data); ip_set = TRUE; - } else if (g_str_equal(key, "ServerRoutes") == TRUE) { - /* XXX: TBD */ + } else if (g_str_equal(key, "ServerRoute") == TRUE) { + server_route_changed(&value, data); } else if (g_str_equal(key, "UserRoutes") == TRUE) { - /* XXX: TBD */ + user_routes_changed(&value, data); } else if (g_str_equal(key, "Nameservers") == TRUE) { extract_nameservers(&value, data); } diff --git a/src/provider.c b/src/provider.c index 64a871b..cd11db9 100644 --- a/src/provider.c +++ b/src/provider.c @@ -38,25 +38,14 @@ static GHashTable *provider_hash = NULL; static GSList *driver_list = NULL; -struct connman_route { - int family; - char *host; - char *netmask; - char *gateway; -}; - struct connman_provider { int refcount; struct connman_service *vpn_service; int index; char *identifier; int family; - GHashTable *routes; struct connman_provider_driver *driver; void *driver_data; - GHashTable *user_routes; - gchar **user_networks; - gsize num_user_networks; }; void __connman_provider_append_properties(struct connman_provider *provider, @@ -109,9 +98,6 @@ static void provider_destruct(struct connman_provider *provider) DBG("provider %p", provider); g_free(provider->identifier); - g_strfreev(provider->user_networks); - g_hash_table_destroy(provider->routes); - g_hash_table_destroy(provider->user_routes); g_free(provider); } @@ -248,6 +234,10 @@ static int set_connected(struct connman_provider *provider, provider_indicate_state(provider, CONNMAN_SERVICE_STATE_READY); + if (provider->driver != NULL && provider->driver->set_routes) + provider->driver->set_routes(provider, + CONNMAN_PROVIDER_ROUTE_ALL); + } else { if (ipconfig != NULL) { provider_indicate_state(provider, @@ -396,13 +386,8 @@ __connman_provider_check_routes(struct connman_provider *provider) if (provider == NULL) return FALSE; - if (provider->user_routes != NULL && - g_hash_table_size(provider->user_routes) > 0) - return TRUE; - - if (provider->routes != NULL && - g_hash_table_size(provider->routes) > 0) - return TRUE; + if (provider->driver != NULL && provider->driver->check_routes) + return provider->driver->check_routes(provider); return FALSE; } @@ -601,27 +586,12 @@ static void provider_offline_mode(connman_bool_t enabled) } -static void destroy_route(gpointer user_data) -{ - struct connman_route *route = user_data; - - g_free(route->host); - g_free(route->netmask); - g_free(route->gateway); - g_free(route); -} - static void provider_initialize(struct connman_provider *provider) { DBG("provider %p", provider); provider->index = 0; provider->identifier = NULL; - provider->user_networks = NULL; - provider->routes = g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, destroy_route); - provider->user_routes = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, destroy_route); } static struct connman_provider *provider_new(void) diff --git a/vpn/vpn-provider.c b/vpn/vpn-provider.c index a5cff3b..7ca3f5d 100644 --- a/vpn/vpn-provider.c +++ b/vpn/vpn-provider.c @@ -75,24 +75,271 @@ struct vpn_provider { char **nameservers; }; +static void append_routes(DBusMessageIter *iter, char **routes) +{ + int i; + + DBG("%p", routes); + + for (i = 0; routes[i] != NULL; i++) { + DBG("routes[%d] %s", i, routes[i]); + dbus_message_iter_append_basic(iter, + DBUS_TYPE_STRING, &routes[i]); + } +} + +static void append_user_routes(DBusMessageIter *iter, void *user_data) +{ + struct vpn_provider *provider = user_data; + + if (provider->user_networks != NULL && + provider->num_user_networks > 0) { + append_routes(iter, provider->user_networks); + } +} + +static void append_server_route(DBusMessageIter *iter, void *user_data) +{ + struct vpn_route *route = user_data; + int family = 0; + + if (route->family == AF_INET) + family = 4; + else if (route->family == AF_INET6) + family = 6; + + if (family != 0) + connman_dbus_dict_append_basic(iter, "ProtocolFamily", + DBUS_TYPE_INT32, &family); + + if (route->host != NULL) + connman_dbus_dict_append_basic(iter, "Host", + DBUS_TYPE_STRING, &route->host); + + if (route->netmask != NULL) + connman_dbus_dict_append_basic(iter, "Netmask", + DBUS_TYPE_STRING, &route->netmask); + + if (route->gateway != NULL) + connman_dbus_dict_append_basic(iter, "Gateway", + DBUS_TYPE_STRING, &route->gateway); +} + +static void send_server_routes(struct vpn_provider *provider, void *user_data) +{ + GHashTableIter hash; + gpointer value, key; + + if (user_data != NULL) { + connman_dbus_property_changed_dict(provider->path, + VPN_CONNECTION_INTERFACE, + "ServerRoute", + append_server_route, + user_data); + return; + } + + g_hash_table_iter_init(&hash, provider->routes); + + while (g_hash_table_iter_next(&hash, &key, &value) == TRUE) + connman_dbus_property_changed_dict(provider->path, + VPN_CONNECTION_INTERFACE, + "ServerRoute", + append_server_route, + value); +} + +static int provider_property_changed(struct vpn_provider *provider, + const char *name, void *user_data) +{ + DBG("provider %p name %s", provider, name); + + if (g_str_equal(name, "UserRoutes") == TRUE) + connman_dbus_property_changed_array(provider->path, + VPN_CONNECTION_INTERFACE, + name, + DBUS_TYPE_STRING, + append_user_routes, + provider); + else if (g_str_equal(name, "ServerRoute") == TRUE) + send_server_routes(provider, user_data); + + return 0; +} + +static char **get_user_networks(DBusMessageIter *array, int *count) +{ + DBusMessageIter entry; + char **networks = NULL; + GSList *list = NULL, *l; + int len; + + dbus_message_iter_recurse(array, &entry); + + while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { + const char *val; + dbus_message_iter_get_basic(&entry, &val); + + list = g_slist_prepend(list, g_strdup(val)); + dbus_message_iter_next(&entry); + } + + len = g_slist_length(list); + if (len == 0) + goto out; + + networks = g_try_new(char *, len + 1); + if (networks == NULL) + goto out; + + *count = len; + networks[len] = 0; + + for (l = list; l != NULL; l = g_slist_next(l)) + networks[--len] = l->data; + +out: + g_slist_free(list); + + return networks; +} + +static void set_user_networks(struct vpn_provider *provider, + char **networks) +{ + int i = 0; + + while (networks[i] != NULL) { + char **elems = g_strsplit(networks[i], "/", 0); + char *network, *netmask; + int family = PF_UNSPEC, ret; + + if (elems == NULL) + break; + + network = elems[0]; + if (network == NULL || *network == '\0') { + DBG("no network/netmask set"); + g_strfreev(elems); + break; + } + + netmask = elems[1]; + if (netmask != NULL && *netmask == '\0') { + DBG("no netmask set"); + g_strfreev(elems); + break; + } + + if (g_strrstr(network, ":") != NULL) + family = AF_INET6; + else if (g_strrstr(network, ".") != NULL) { + family = AF_INET; + + if (g_strrstr(netmask, ".") == NULL) { + /* We have netmask length */ + in_addr_t addr; + struct in_addr netmask_in; + unsigned char prefix_len = 32; + + if (netmask != NULL) + prefix_len = atoi(netmask); + + addr = 0xffffffff << (32 - prefix_len); + netmask_in.s_addr = htonl(addr); + netmask = inet_ntoa(netmask_in); + + DBG("network %s netmask %s", network, netmask); + } + } + + ret = __vpn_provider_append_user_route(provider, + family, network, netmask); + g_strfreev(elems); + + if (ret != 0) + break; + + i++; + } +} + +static void del_routes(struct vpn_provider *provider) +{ + g_hash_table_remove_all(provider->user_routes); + g_strfreev(provider->user_networks); + provider->user_networks = NULL; + provider->num_user_networks = 0; +} + static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg, void *data) { + struct vpn_provider *provider = data; + DBusMessageIter iter, value; + const char *name; + int type; + DBG("conn %p", conn); - // XXX: + if (dbus_message_iter_init(msg, &iter) == FALSE) + return __connman_error_invalid_arguments(msg); - return NULL; + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return __connman_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&iter, &name); + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) + return __connman_error_invalid_arguments(msg); + + dbus_message_iter_recurse(&iter, &value); + + type = dbus_message_iter_get_arg_type(&value); + + if (g_str_equal(name, "UserRoutes") == TRUE) { + char **networks; + int count = 0; + + if (type != DBUS_TYPE_ARRAY) + return __connman_error_invalid_arguments(msg); + + networks = get_user_networks(&value, &count); + if (networks != NULL) { + g_strfreev(provider->user_networks); + provider->user_networks = networks; + provider->num_user_networks = count; + set_user_networks(provider, provider->user_networks); + + provider_property_changed(provider, name, NULL); + } + } else + return __connman_error_invalid_property(msg); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *clear_property(DBusConnection *conn, DBusMessage *msg, void *data) { + struct vpn_provider *provider = data; + const char *name; + DBG("conn %p", conn); - // XXX: + dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID); - return NULL; + if (g_str_equal(name, "UserRoutes") == TRUE) { + del_routes(provider); + + provider_property_changed(provider, name, NULL); + } else { + return __connman_error_invalid_property(msg); + } + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *do_connect(DBusConnection *conn, DBusMessage *msg, @@ -231,66 +478,6 @@ int __vpn_provider_append_user_route(struct vpn_provider *provider, return 0; } -static void set_user_networks(struct vpn_provider *provider, - char **networks) -{ - int i = 0; - - while (networks[i] != NULL) { - char **elems = g_strsplit(networks[i], "/", 0); - char *network, *netmask; - int family = PF_UNSPEC, ret; - - if (elems == NULL) - break; - - network = elems[0]; - if (network == NULL || *network == '\0') { - DBG("no network/netmask set"); - g_strfreev(elems); - break; - } - - netmask = elems[1]; - if (netmask != NULL && *netmask == '\0') { - DBG("no netmask set"); - g_strfreev(elems); - break; - } - - if (g_strrstr(network, ":") != NULL) - family = AF_INET6; - else if (g_strrstr(network, ".") != NULL) { - family = AF_INET; - - if (g_strrstr(netmask, ".") == NULL) { - /* We have netmask length */ - in_addr_t addr; - struct in_addr netmask_in; - unsigned char prefix_len = 32; - - if (netmask != NULL) - prefix_len = atoi(netmask); - - addr = 0xffffffff << (32 - prefix_len); - netmask_in.s_addr = htonl(addr); - netmask = inet_ntoa(netmask_in); - - DBG("network %s netmask %s", network, netmask); - } - } - - ret = __vpn_provider_append_user_route(provider, - family, network, netmask); - g_strfreev(elems); - - if (ret != 0) - break; - - i++; - } -} - static int provider_load_from_keyfile(struct vpn_provider *provider, GKeyFile *keyfile) { @@ -1102,43 +1289,6 @@ static void provider_create_all_from_type(const char *provider_type) g_strfreev(providers); } -static char **get_user_networks(DBusMessageIter *array, int *count) -{ - DBusMessageIter entry; - char **networks = NULL; - GSList *list = NULL, *l; - int len; - - dbus_message_iter_recurse(array, &entry); - - while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { - const char *val; - dbus_message_iter_get_basic(&entry, &val); - - list = g_slist_prepend(list, g_strdup(val)); - dbus_message_iter_next(&entry); - } - - len = g_slist_length(list); - if (len == 0) - goto out; - - networks = g_try_new(char *, len + 1); - if (networks == NULL) - goto out; - - *count = len; - networks[len] = 0; - - for (l = list; l != NULL; l = g_slist_next(l)) - networks[--len] = l->data; - -out: - g_slist_free(list); - - return networks; -} - int __vpn_provider_create_and_connect(DBusMessage *msg) { struct vpn_provider *provider; -- 1.7.11.4 _______________________________________________ connman mailing list connman@connman.net http://lists.connman.net/listinfo/connman