Commit fe15fc16 authored by Jussi Laakkonen's avatar Jussi Laakkonen

[connman-vpn] Add default route option for VPN. Fixes JB#41902

This commit enables to define a "DefaultRoute" string for a provider in
VPN plugin that tells ConnMan not to use the VPN as default route. In
VPN plugin this can be set with vpn_provider_set_string(), value "false"
means that the VPN is not used as default route with ConnMan
connections.

The "DefaultRoute" string is sent by the VPN property_changed() to
ConnMan if the value for the string is set. If string has no value
nothing will be sent.

This string is handled by ConnMan as configuration strings
and src/provider.c has a function to check if the provider is being set
as default route or not. By default, it always reports true if the
string is not set.

If the VPN service having "DefaultRoute" as "false" is either attempted
to be switched as current_default or as default in service list the
service below the VPN service is set as current_default/default in
service list.

Additionally, moved is_any_addr() from vpn/vpn-provider.c to
src/ipaddress.c since it is used also elsewhere.
parent ce0c09e6
......@@ -1804,6 +1804,10 @@ static gboolean property_changed(DBusConnection *conn,
g_free(data->domain);
data->domain = g_strdup(str);
connman_provider_set_domain(data->provider, data->domain);
} else if (g_str_equal(key, "DefaultRoute")) {
dbus_message_iter_get_basic(&value, &str);
g_hash_table_replace(data->setting_strings,
g_strdup(key), g_strdup(str));
}
if (ip_set && err == 0) {
......
......@@ -143,9 +143,19 @@ static void get_gateway_cb(const char *gateway, int index, void *user_data)
struct gateway_data *data;
struct get_gateway_params *params = user_data;
int family;
struct connman_service *service;
if (index < 0)
goto out;
service = __connman_service_lookup_from_index(params->vpn_index);
if (!__connman_service_is_default_route(service)) {
DBG("Not setting gateway config of non default service %s",
service ? __connman_service_get_ident(service) : "");
goto out;
}
DBG("phy index %d phy gw %s vpn index %d vpn gw %s", index, gateway,
params->vpn_index, params->vpn_gateway);
......@@ -412,6 +422,12 @@ static void set_default_gateway(struct gateway_data *data,
int index;
int status4 = 0, status6 = 0;
bool do_ipv4 = false, do_ipv6 = false;
if (data && !__connman_service_is_default_route(data->service)) {
DBG("Not setting default gateway for %s",
__connman_service_get_ident(data->service));
return;
}
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
do_ipv4 = true;
......@@ -845,6 +861,12 @@ int __connman_connection_gateway_add(struct connman_service *service,
set_vpn_routes(new_gateway, service, gateway, type, peer,
active_gateway);
if (!__connman_service_is_default_route(service)) {
DBG("Not adding gateways for non default VPN %s",
service ? __connman_service_get_ident(service) :
"");
goto done;
}
} else {
if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
new_gateway->ipv4_gateway)
......@@ -978,6 +1000,23 @@ bool __connman_connection_update_gateway(void)
default_gateway = find_default_gateway();
DBG("default %p", default_gateway);
if (default_gateway &&
!__connman_service_is_default_route(default_gateway->service)) {
DBG("Not updating gateway with non default route %s",
default_gateway->service ?
__connman_service_get_ident(default_gateway->service) :
"");
if (default_gateway->ipv4_gateway)
unset_default_gateway(default_gateway,
CONNMAN_IPCONFIG_TYPE_IPV4);
if (default_gateway->ipv6_gateway)
unset_default_gateway(default_gateway,
CONNMAN_IPCONFIG_TYPE_IPV6);
return true;
}
/*
* There can be multiple active gateways so we need to
......
......@@ -341,6 +341,8 @@ struct connman_ipconfig *__connman_ipconfig_create(int index,
#define __connman_ipconfig_unref(ipconfig) \
__connman_ipconfig_unref_debug(ipconfig, __FILE__, __LINE__, __func__)
bool __connman_ipaddress_is_any_addr(const char *address, int family);
struct connman_ipconfig *
__connman_ipconfig_ref_debug(struct connman_ipconfig *ipconfig,
const char *file, int line, const char *caller);
......@@ -667,6 +669,7 @@ void __connman_ipv6pd_cleanup(void);
#include <connman/provider.h>
bool __connman_provider_is_default_route(struct connman_provider *provider);
bool __connman_provider_check_routes(struct connman_provider *provider);
int __connman_provider_append_user_route(struct connman_provider *provider,
int family, const char *network, const char *netmask);
......@@ -819,6 +822,7 @@ void __connman_service_set_domainname(struct connman_service *service,
const char *__connman_service_get_nameserver(struct connman_service *service);
void __connman_service_set_proxy_autoconfig(struct connman_service *service,
const char *url);
bool __connman_service_is_default_route(struct connman_service *service);
void __connman_service_set_identity(struct connman_service *service,
const char *identity);
......
......@@ -27,6 +27,7 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <glib.h>
#include <connman/ipaddress.h>
......@@ -226,3 +227,37 @@ connman_ipaddress_copy(struct connman_ipaddress *ipaddress)
return copy;
}
bool __connman_ipaddress_is_any_addr(const char *address, int family)
{
bool rval = false;
struct addrinfo hints;
struct addrinfo *result = NULL;
struct sockaddr_in6 *in6 = NULL;
struct sockaddr_in *in4 = NULL;
if (!address || !*address)
goto out;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = family;
if (getaddrinfo(address, NULL, &hints, &result))
goto out;
if (result) {
if (result->ai_family == AF_INET6) {
in6 = (struct sockaddr_in6*)result->ai_addr;
rval = IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr);
} else {
in4 = (struct sockaddr_in*)result->ai_addr;
rval = in4->sin_addr.s_addr == INADDR_ANY;
}
freeaddrinfo(result);
}
out:
return rval;
}
......@@ -52,7 +52,7 @@ struct connman_provider {
void __connman_provider_append_properties(struct connman_provider *provider,
DBusMessageIter *iter)
{
const char *host, *domain, *type;
const char *host, *domain, *type, *defaultroute;
if (!provider->driver || !provider->driver->get_property)
return;
......@@ -60,6 +60,7 @@ void __connman_provider_append_properties(struct connman_provider *provider,
host = provider->driver->get_property(provider, "Host");
domain = provider->driver->get_property(provider, "Domain");
type = provider->driver->get_property(provider, "Type");
defaultroute = provider->driver->get_property(provider, "DefaultRoute");
if (host)
connman_dbus_dict_append_basic(iter, "Host",
......@@ -72,6 +73,11 @@ void __connman_provider_append_properties(struct connman_provider *provider,
if (type)
connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
&type);
if (defaultroute)
connman_dbus_dict_append_basic(iter, "DefaultRoute",
DBUS_TYPE_STRING,
&defaultroute);
}
struct connman_provider *
......@@ -450,6 +456,26 @@ const char *connman_provider_get_string(struct connman_provider *provider,
return NULL;
}
bool __connman_provider_is_default_route(struct connman_provider *provider)
{
const char* default_route = NULL;
if (!provider)
return true;
default_route = connman_provider_get_string(provider, "DefaultRoute");
DBG("DefaultRoute:%s", default_route);
if (!default_route)
return true;
if (g_ascii_strcasecmp(default_route, "true") != 0)
return false;
return true;
}
bool
__connman_provider_check_routes(struct connman_provider *provider)
{
......
......@@ -1750,6 +1750,23 @@ static void disconnect_vpn_service(struct connman_service *service, void* user_d
}
}
struct connman_service *get_connected_default_route_service()
{
GList *iter;
struct connman_service *service = NULL;
for (iter = service_list; iter; iter = iter->next) {
service = iter->data;
if (__connman_service_is_default_route(service) &&
is_connected(service))
break;
}
return service;
}
static void default_changed(void)
{
struct connman_service *service = __connman_service_get_default();
......@@ -1760,6 +1777,24 @@ static void default_changed(void)
DBG("current default %p %s", current_default,
current_default ? current_default->identifier : "");
DBG("new default %p %s", service, service ? service->identifier : "");
if (!__connman_service_is_default_route(service)) {
/*
* If the current_default is not connected and the new service
* is not the default route, find the next connected from
* services list and use that service as new default
*/
if (current_default && !is_connected(current_default)) {
service = get_connected_default_route_service();
DBG("Selected new default service %s",
service ? service->identifier : "");
} else {
DBG("Not setting %s as default service",
service->identifier);
return;
}
}
__connman_service_timeserver_changed(current_default, NULL);
......@@ -3292,6 +3327,16 @@ void __connman_service_set_proxy_autoconfig(struct connman_service *service,
__connman_notifier_proxy_changed(service);
}
bool __connman_service_is_default_route(struct connman_service *service)
{
if (!service)
return true;
DBG("");
return __connman_provider_is_default_route(service->provider);
}
const char *connman_service_get_proxy_autoconfig(struct connman_service *service)
{
if (!service)
......@@ -5241,6 +5286,19 @@ static void switch_default_service(struct connman_service *default_service,
{
struct connman_service *service;
GList *src, *dst;
/*
* If the service to be used as downgrade service is not set as default
* route revert the order of the services.
*/
if (!__connman_service_is_default_route(downgrade_service)) {
DBG("Not switching non default route downgrade service "
"default_service=%s downgrade_service=%s",
default_service ? default_service->identifier : "",
downgrade_service ? downgrade_service->identifier : "");
switch_default_service(downgrade_service, default_service);
return;
}
apply_relevant_default_downgrade(default_service);
src = g_list_find(service_list, downgrade_service);
......
......@@ -159,6 +159,28 @@ static void free_setting(gpointer data)
g_free(setting);
}
static bool provider_is_default_route(struct vpn_provider *provider)
{
const gchar *key = "DefaultRoute";
const gchar *value = NULL;
/* Default action with routes is always true */
if (!provider)
return true;
value = vpn_provider_get_string(provider, key);
if (value && *value) {
DBG("Provider %p key %s set %s", provider, key, value);
if (g_ascii_strcasecmp(value, "true") != 0)
return false;
}
return true;
}
static void append_route(DBusMessageIter *iter, void *user_data)
{
struct vpn_route *route = user_data;
......@@ -1453,6 +1475,7 @@ static int provider_indicate_state(struct vpn_provider *provider,
enum vpn_provider_state state)
{
const char *str;
const char *route_value;
enum vpn_provider_state old_state;
str = state2string(state);
......@@ -1489,6 +1512,21 @@ static int provider_indicate_state(struct vpn_provider *provider,
"Domain",
DBUS_TYPE_STRING,
&provider->domain);
route_value = vpn_provider_get_string(provider, "DefaultRoute");
/*
* "DefaultRoute" has to be explicitely set in provider strings
* in order to be sent. If "DefaultRoute" is not set it defaults
* to true.
*/
if (route_value && *route_value) {
connman_dbus_property_changed_basic(provider->path,
VPN_CONNECTION_INTERFACE,
"DefaultRoute",
DBUS_TYPE_STRING,
&route_value);
}
}
if (old_state != state)
......@@ -1630,40 +1668,6 @@ static bool check_host(char **hosts, char *host)
return false;
}
static bool is_any_addr(const char *address, int family)
{
bool rval = false;
struct addrinfo hints;
struct addrinfo *result = NULL;
struct sockaddr_in6 *in6 = NULL;
struct sockaddr_in *in4 = NULL;
if (!address || !*address)
goto out;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = family;
if (getaddrinfo(address, NULL, &hints, &result))
goto out;
if (result) {
if (result->ai_family == AF_INET6) {
in6 = (struct sockaddr_in6*)result->ai_addr;
rval = IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr);
} else {
in4 = (struct sockaddr_in*)result->ai_addr;
rval = in4->sin_addr.s_addr == INADDR_ANY;
}
freeaddrinfo(result);
}
out:
return rval;
}
static void provider_append_routes(gpointer key, gpointer value,
gpointer user_data)
{
......@@ -1689,11 +1693,20 @@ static void provider_append_routes(gpointer key, gpointer value,
* When network and netmask are INADDR_ANY and gateway is NULL then
* default route is being added
*/
if (is_any_addr(route->network, provider->family) == 0 &&
if (__connman_ipaddress_is_any_addr(route->network, provider->family) &&
!route->gateway &&
is_any_addr(route->netmask, provider->family) == 0) {
DBG("Adding default route for provider %p", provider);
default_route_set = true;
__connman_ipaddress_is_any_addr(route->netmask,
provider->family)) {
if (provider_is_default_route(provider)) {
DBG("Add default route for provider %p", provider);
default_route_set = true;
} else {
DBG("Not adding default route."
"Provider %p is not set as default route.",
provider);
return;
}
}
if (route->family == AF_INET6) {
......@@ -1741,7 +1754,11 @@ static int set_connected(struct vpn_provider *provider,
g_hash_table_foreach(provider->user_routes,
provider_append_routes, provider);
if (!default_route_set) {
/*
* If the provider is set to be default route and default route
* has not been set, add the default route.
*/
if (provider_is_default_route(provider) && !default_route_set) {
DBG("Adding default route for provider %p", provider);
if (provider->family == AF_INET6) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment