Naveen Singh <naveensingh0977@gmail.com> <naveensingh0977@gmail.com>
Mylène Josserand <josserand.mylene@gmail.com> <josserand.mylene@gmail.com>
Måns Rullgård <mans@mansr.com> <mans@mansr.com>
+Emmanuel VAUTRIN <Emmanuel.VAUTRIN@cpexterne.org> <Emmanuel.VAUTRIN@cpexterne.org>
+Simon Holesch <Simon.Holesch@bshg.com> <Simon.Holesch@bshg.com>
+Nishant Chaprana <n.chaprana@samsung.com> <n.chaprana@samsung.com>
Matt Vogt <matthew.vogt@jollamobile.com>
David Llewellyn-Jones <david.llewellyn-jones@jolla.com>
David Weidenkopf <David.Weidenkopf@Arthrex.com>
+Maxime Roussin-Bélanger <maxime.roussinbelanger@gmail.com>
+Holesch, Simon (GED-SDD1) <Simon.Holesch@bshg.com>
+Christoph Steiger <c.steiger@lemonage.de>
+Markus Held <mjh42@gmx.de>
+Sergey Matyukevich <geomatsi@gmail.com>
+Pieter Cardoen <P.Cardoen@TELEVIC.com>
+Emmanuel Vautrin <emmanuel.vautrin@cpexterne.org>
+Boleslaw Tokarski <boleslaw.tokarski@jolla.com>
+Gabriel FORTE <gforte@wyplay.com>
+Colin Wee <cwee@tesla.com>
+Valery Kashcheev <v.kascheev@omp.ru>
+Alyssa Ross <hi@alyssa.is>
+ver 1.40:
+ Fix issue with handling WiFi disconnecting status.
+ Fix issue with handling WiFi auto-connect and iwd backend.
+ Fix issue with DNS Proxy stack-based buffer overflow attack.
+
+ver 1.39:
+ Fix issue with scanning state synchronization and iwd.
+ Fix issue with invalid key with 4-way handshake offloading.
+ Fix issue with DNS proxy length checks to prevent buffer overflow.
+ Fix issue with DHCP leaking stack data via uninitialized variable.
+
ver 1.38:
Fix issue with online check on IP address update.
Fix issue with OpenVPN and encrypted private keys.
nodist_include_HEADERS = include/version.h
noinst_HEADERS = include/rtnl.h include/task.h \
- include/dbus.h include/option.h \
+ include/dbus.h \
include/provider.h include/vpn-dbus.h \
include/utsname.h include/timeserver.h include/proxy.h \
include/technology.h include/setting.h \
vpn/vpn-ipconfig.c src/inet.c vpn/vpn-rtnl.c \
src/dbus.c src/storage.c src/ipaddress.c src/agent.c \
vpn/vpn-agent.c vpn/vpn-agent.h src/inotify.c \
- vpn/vpn-config.c vpn/vpn-settings.c
+ vpn/vpn-config.c vpn/vpn-settings.c vpn/vpn-util.c
vpn_connman_vpnd_LDADD = gdbus/libgdbus-internal.la $(builtin_vpn_libadd) \
@GLIB_LIBS@ @DBUS_LIBS@ @GNUTLS_LIBS@ \
if OPENCONNECT_BUILTIN
builtin_vpn_modules += openconnect
builtin_vpn_sources += vpn/plugins/openconnect.c
-builtin_vpn_cflags += -DOPENCONNECT=\"@OPENCONNECT@\"
+builtin_vpn_cflags += -DOPENCONNECT=\"@OPENCONNECT@\" \
+ @LIBOPENCONNECT_CFLAGS@
+builtin_vpn_libadd += @LIBOPENCONNECT_LIBS@
else
vpn_plugin_LTLIBRARIES += vpn/plugins/openconnect.la
vpn_plugin_objects += $(plugins_openconnect_la_OBJECTS)
vpn_plugins_openconnect_la_CFLAGS = $(plugin_cflags) \
-DOPENCONNECT=\"@OPENCONNECT@\" \
-DVPN_STATEDIR=\""$(vpn_statedir)"\" \
- -DSCRIPTDIR=\""$(build_scriptdir)"\"
+ -DSCRIPTDIR=\""$(build_scriptdir)"\" \
+ @LIBOPENCONNECT_CFLAGS@
vpn_plugins_openconnect_la_LDFLAGS = $(plugin_ldflags)
+vpn_plugins_openconnect_la_LIBADD = @LIBOPENCONNECT_LIBS@
endif
endif
===========
Mailing list:
- connman@connman.net
+ connman@lists.linux.dev
-For additional information about the project visit ConnMan web site:
- https://01.org/connman
- http://www.connman.net
+If you would like to subscribe to receive mail in your inbox, just
+send a (empty) message from your email account to
-You can report bugs at https://01.org/jira/browse/CM
+ connman+subscribe@lists.linux.dev
+
+Mailing list archive:
+ https://lore.kernel.org/connman
AC_PREREQ(2.60)
-AC_INIT(connman, 1.38)
+AC_INIT(connman, 1.40)
AC_CONFIG_MACRO_DIR([m4])
OPENCONNECT="${path_openconnect}"
AC_SUBST(OPENCONNECT)
fi
+ PKG_CHECK_MODULES(LIBOPENCONNECT, openconnect >= 8, [],
+ AC_MSG_ERROR(openconnect >= 8 is required))
fi
AM_CONDITIONAL(OPENCONNECT, test "${enable_openconnect}" != "no")
AM_CONDITIONAL(OPENCONNECT_BUILTIN, test "${enable_openconnect}" = "builtin")
This list of servers is used when TimeUpdates is set
to auto.
+
+ boolean TimeserverSynced [readonly] [experimental]
+
+ This value indicates if the current system time
+ is synced via NTP servers.
+
+ True when TimeUpdates is set to auto and Time value
+ results from the system time synchronization with a NTP
+ server. Otherwise False.
.B OpenVPN.NSCertType=client \fR|\fB server
Peer certificate type, either \fBclient\fP or \fBserver\fP.
.TP
-.BI OpenVPN.Protocol= protocol
+.BI OpenVPN.Proto= protocol
Use \fIprotocol\fP.
.TP
.BI OpenVPN.Port= port
.RB [\| \-P
.IR plugin [,...]\|]
.RB [\| \-n \|]
-.RB [\| \-r \|]
.SH DESCRIPTION
The \fIConnMan-VPN\fP provides a daemon for managing vpn connections together
with \fBconnmand\fP(8). The Connection Manager is designed to be slim and to
.BR \-n ", " \-\-nodaemon
Do not daemonize. This is useful for debugging, and directs log output to
the controlling terminal in addition to syslog.
-.TP
-.BR \-r ", " \-\-routes
-Manage VPN routes instead of telling \fBconnmand\fP(8) to do it.
.SH SEE ALSO
.BR connmanctl (1), \ connman-vpn.conf (5), \c
.BR \ connman-vpn-provider.config (5), \ connmand (8)
If this setting is false, the default service will remain in READY state.
Default value is true.
.TP
+.BI OnlineCheckInitialInterval= secs, OnlineCheckMaxInterval= secs
+Range of intervals between two online check requests.
+When an online check request fails, another one is triggered after a
+longer interval. The intervals follow the power of two series of numbers
+between OnlineCheckInitialInterval and OnlineCheckMaxInterval.
+Default range is [1, 12], corresponding to the following intervals, in
+seconds: 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121 and 144.
+.TP
+.BI EnableOnlineToReadyTransition=true\ \fR|\fB\ false
+WARNING: Experimental feature!!!
+In addition to EnableOnlineCheck setting, enable or disable use of HTTP GET
+to detect the loss of end-to-end connectivity.
+If this setting is false, when the default service transitions to ONLINE
+state, the HTTP GET request is no more called until next cycle, initiated
+by a transition of the default service to DISCONNECT state.
+If this setting is true, the HTTP GET request keeps beeing called to guarantee
+that end-to-end connectivity is still successful. If not, the default service
+will transition to READY state, enabling another service to become the
+default one, in replacement.
+Default value is false.
+.TP
.BI AutoConnectRoamingServices=true\ \fR|\fB\ false
Automatically connect roaming services. This is not recommended unless you know
you won't have any billing problem.
VPN related parameters (M = mandatory, O = optional):
- Name: A user defined name for the VPN (M)
- Host: VPN server IP address (M)
-- Domain: Domain name for the VPN service (M)
+- Domain: Domain name for the VPN service (O)
- Networks: The networks behind the VPN link can be defined here. This can
be missing if all traffic should go via VPN tunnel. If there are more
than one network, then separate them by comma. Format of the entry
configured externally via a configuration file.
The only valid operation are Connect(), Disconnect()
- and GetProperties()
+ and GetProperties()
+
+ boolean SplitRouting
+
+ This value reflects the split routing setting on
+ connmand side. By default, this value is omitted and
+ defaults to false. The value needs to be explicitly
+ set to true for VPN to be split routed.
int Index [readonly]
return NULL;
}
-static void format_rule(struct filter_data *data, char *rule, size_t size)
+static char *format_rule(struct filter_data *data)
{
+ char *rule, *tmp;
const char *sender;
- int offset;
- offset = snprintf(rule, size, "type='signal'");
+ rule = g_strdup("type='signal'");
sender = data->name ? : data->owner;
- if (sender)
- offset += snprintf(rule + offset, size - offset,
- ",sender='%s'", sender);
- if (data->path)
- offset += snprintf(rule + offset, size - offset,
- ",path='%s'", data->path);
- if (data->interface)
- offset += snprintf(rule + offset, size - offset,
- ",interface='%s'", data->interface);
- if (data->member)
- offset += snprintf(rule + offset, size - offset,
- ",member='%s'", data->member);
- if (data->argument)
- snprintf(rule + offset, size - offset,
- ",arg0='%s'", data->argument);
+ if (sender) {
+ tmp = rule;
+ rule = g_strdup_printf("%s,sender='%s'", rule, sender);
+ g_free(tmp);
+ }
+
+ if (data->path) {
+ tmp = rule;
+ rule = g_strdup_printf("%s,path='%s'", rule, data->path);
+ g_free(tmp);
+ }
+
+ if (data->interface){
+ tmp = rule;
+ rule = g_strdup_printf("%s,interface='%s'", rule,
+ data->interface);
+ g_free(tmp);
+ }
+
+ if (data->member) {
+ tmp = rule;
+ rule = g_strdup_printf("%s,member='%s'", rule, data->member);
+ g_free(tmp);
+ }
+
+ if (data->argument) {
+ tmp = rule;
+ rule = g_strdup_printf("%s,arg0='%s'", rule, data->argument);
+ g_free(tmp);
+ }
+
+ return rule;
}
static gboolean add_match(struct filter_data *data,
DBusHandleMessageFunction filter)
{
DBusError err;
- char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+ char *rule;
- format_rule(data, rule, sizeof(rule));
+ rule = format_rule(data);
dbus_error_init(&err);
dbus_bus_add_match(data->connection, rule, &err);
error("Adding match rule \"%s\" failed: %s", rule,
err.message);
dbus_error_free(&err);
+ g_free(rule);
return FALSE;
}
data->handle_func = filter;
data->registered = TRUE;
+ g_free(rule);
return TRUE;
}
static gboolean remove_match(struct filter_data *data)
{
DBusError err;
- char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+ char *rule;
- format_rule(data, rule, sizeof(rule));
+ rule = format_rule(data);
dbus_error_init(&err);
error("Removing owner match rule for %s failed: %s",
rule, err.message);
dbus_error_free(&err);
+ g_free(rule);
return FALSE;
}
+ g_free(rule);
return TRUE;
}
NULL);
}
-static uint32_t get_lease(struct dhcp_packet *packet)
+static uint32_t get_lease(struct dhcp_packet *packet, uint16_t packet_len)
{
uint8_t *option;
uint32_t lease_seconds;
- option = dhcp_get_option(packet, DHCP_LEASE_TIME);
+ option = dhcp_get_option(packet, packet_len, DHCP_LEASE_TIME);
if (!option)
return 3600;
}
}
-static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
+static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet,
+ uint16_t packet_len)
{
GDHCPOptionType type;
GList *list, *value_list;
for (list = dhcp_client->request_list; list; list = list->next) {
code = (uint8_t) GPOINTER_TO_INT(list->data);
- option = dhcp_get_option(packet, code);
+ option = dhcp_get_option(packet, packet_len, code);
if (!option) {
g_hash_table_remove(dhcp_client->code_value_hash,
GINT_TO_POINTER((int) code));
{
GDHCPClient *dhcp_client = user_data;
struct sockaddr_in dst_addr = { 0 };
- struct dhcp_packet packet;
+ struct dhcp_packet packet = { 0 };
struct dhcpv6_packet *packet6 = NULL;
uint8_t *message_type = NULL, *client_id = NULL, *option,
*server_id = NULL;
re = dhcp_recv_l2_packet(&packet,
dhcp_client->listener_sockfd,
&dst_addr);
+ pkt_len = (uint16_t)(unsigned int)re;
xid = packet.xid;
} else if (dhcp_client->listen_mode == L3) {
if (dhcp_client->type == G_DHCP_IPV6) {
dhcp_client->status_code = status;
}
} else {
- message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
+ message_type = dhcp_get_option(&packet, pkt_len, DHCP_MESSAGE_TYPE);
if (!message_type)
return TRUE;
}
dhcp_client->timeout = 0;
dhcp_client->retry_times = 0;
- option = dhcp_get_option(&packet, DHCP_SERVER_ID);
+ option = dhcp_get_option(&packet, pkt_len, DHCP_SERVER_ID);
dhcp_client->server_ip = get_be32(option);
dhcp_client->requested_ip = ntohl(packet.yiaddr);
remove_timeouts(dhcp_client);
- dhcp_client->lease_seconds = get_lease(&packet);
+ dhcp_client->lease_seconds = get_lease(&packet, pkt_len);
- get_request(dhcp_client, &packet);
+ get_request(dhcp_client, &packet, pkt_len);
switch_listening_mode(dhcp_client, L_NONE);
dhcp_client->assigned_ip = get_ip(packet.yiaddr);
if (dhcp_client->state == REBOOTING) {
- option = dhcp_get_option(&packet,
+ option = dhcp_get_option(&packet, pkt_len,
DHCP_SERVER_ID);
dhcp_client->server_ip = get_be32(option);
}
return OPTION_UNKNOWN;
}
-uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
+uint8_t *dhcp_get_option(struct dhcp_packet *packet, uint16_t packet_len, int code)
{
int len, rem;
- uint8_t *optionptr;
+ uint8_t *optionptr, *options_end;
+ size_t options_len;
uint8_t overload = 0;
/* option bytes: [code][len][data1][data2]..[dataLEN] */
optionptr = packet->options;
rem = sizeof(packet->options);
+ options_len = packet_len - (sizeof(*packet) - sizeof(packet->options));
+ options_end = optionptr + options_len - 1;
while (1) {
- if (rem <= 0)
+ if ((rem <= 0) && (optionptr + OPT_CODE > options_end))
/* Bad packet, malformed option field */
return NULL;
break;
}
+ if (optionptr + OPT_LEN > options_end) {
+ /* bad packet, would read length field from OOB */
+ return NULL;
+ }
+
len = 2 + optionptr[OPT_LEN];
rem -= len;
if (rem < 0)
continue; /* complain and return NULL */
- if (optionptr[OPT_CODE] == code)
- return optionptr + OPT_DATA;
+ if (optionptr[OPT_CODE] == code) {
+ if (optionptr + len > options_end) {
+ /* bad packet, option length points OOB */
+ return NULL;
+ } else {
+ return optionptr + OPT_DATA;
+ }
+ }
if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD)
overload |= optionptr[OPT_DATA];
};
#endif
-uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code);
+uint8_t *dhcp_get_option(struct dhcp_packet *packet, uint16_t packet_len, int code);
uint8_t *dhcpv6_get_option(struct dhcpv6_packet *packet, uint16_t pkt_len,
int code, uint16_t *option_len, int *option_count);
uint8_t *dhcpv6_get_sub_option(unsigned char *option, uint16_t max_len,
}
-static uint8_t check_packet_type(struct dhcp_packet *packet)
+static uint8_t check_packet_type(struct dhcp_packet *packet, uint16_t packet_len)
{
uint8_t *type;
if (packet->op != BOOTREQUEST)
return 0;
- type = dhcp_get_option(packet, DHCP_MESSAGE_TYPE);
+ type = dhcp_get_option(packet, packet_len, DHCP_MESSAGE_TYPE);
if (!type)
return 0;
struct dhcp_lease *lease;
uint32_t requested_nip = 0;
uint8_t type, *server_id_option, *request_ip_option;
+ uint16_t packet_len;
int re;
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd);
if (re < 0)
return TRUE;
+ packet_len = (uint16_t)(unsigned int)re;
- type = check_packet_type(&packet);
+ type = check_packet_type(&packet, packet_len);
if (type == 0)
return TRUE;
- server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
+ server_id_option = dhcp_get_option(&packet, packet_len, DHCP_SERVER_ID);
if (server_id_option) {
uint32_t server_nid =
get_unaligned((const uint32_t *) server_id_option);
return TRUE;
}
- request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP);
+ request_ip_option = dhcp_get_option(&packet, packet_len, DHCP_REQUESTED_IP);
if (request_ip_option)
requested_nip = get_be32(request_ip_option);
int connman_inet_set_ipv6_address(int index,
struct connman_ipaddress *ipaddress);
int connman_inet_clear_ipv6_address(int index,
- const char *address, int prefix_len);
+ struct connman_ipaddress *ipaddress);
int connman_inet_add_ipv6_network_route(int index, const char *host,
const char *gateway, unsigned char prefix_len);
int connman_inet_add_ipv6_host_route(int index, const char *host,
int connman_inet_check_ipaddress(const char *host);
bool connman_inet_check_hostname(const char *ptr, size_t len);
bool connman_inet_is_ipv6_supported();
+bool connman_inet_is_default_route(int family, const char *host,
+ const char *gateway, const char *netmask);
+
+int connman_inet_get_route_addresses(int index, char **network, char **netmask,
+ char **destination);
+int connman_inet_ipv6_get_route_addresses(int index, char **network,
+ char **netmask,
+ char **destination);
#ifdef __cplusplus
}
#ifndef __CONNMAN_IPADDRESS_H
#define __CONNMAN_IPADDRESS_H
+#include <stdbool.h>
+
#ifdef __cplusplus
extern "C" {
#endif
unsigned char connman_ipaddress_calc_netmask_len(const char *netmask);
struct connman_ipaddress *connman_ipaddress_alloc(int family);
+void connman_ipaddress_set_p2p(struct connman_ipaddress *ipaddress,
+ bool value);
void connman_ipaddress_free(struct connman_ipaddress *ipaddress);
int connman_ipaddress_set_ipv4(struct connman_ipaddress *ipaddress,
const char *address, const char *netmask,
int connman_network_set_wifi_channel(struct connman_network *network,
uint16_t channel);
uint16_t connman_network_get_wifi_channel(struct connman_network *network);
+int connman_network_set_autoconnect(struct connman_network *network,
+ bool autoconnect);
int connman_network_set_string(struct connman_network *network,
const char *key, const char *value);
void (*remove) (struct connman_network *network);
int (*connect) (struct connman_network *network);
int (*disconnect) (struct connman_network *network);
+ int (*set_autoconnect) (struct connman_network *network,
+ bool autoconnect);
};
int connman_network_driver_register(struct connman_network_driver *driver);
+++ /dev/null
-/*
- *
- * Connection Manager
- *
- * Copyright (C) 2007-2012 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
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifndef __CONNMAN_OPTION_H
-#define __CONNMAN_OPTION_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-const char *connman_option_get_string(const char *key);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __CONNMAN_OPTION_H */
char * const *nameservers);
void connman_provider_set_autoconnect(struct connman_provider *provider,
bool flag);
+bool connman_provider_is_split_routing(struct connman_provider *provider);
+int connman_provider_set_split_routing(struct connman_provider *provider,
+ bool split_routing);
+int connman_provider_get_family(struct connman_provider *provider);
const char *connman_provider_get_driver_name(struct connman_provider *provider);
const char *connman_provider_get_save_group(struct connman_provider *provider);
CONNMAN_SERVICE_CONNECT_REASON_AUTO = 1,
CONNMAN_SERVICE_CONNECT_REASON_USER = 2,
CONNMAN_SERVICE_CONNECT_REASON_SESSION = 3,
+ CONNMAN_SERVICE_CONNECT_REASON_NATIVE = 4,
};
struct connman_service;
#endif
bool connman_setting_get_bool(const char *key);
+unsigned int connman_setting_get_uint(const char *key);
+char *connman_setting_get_string(const char *key);
char **connman_setting_get_string_list(const char *key);
unsigned int *connman_setting_get_uint_list(const char *key);
const char *method;
bool result;
- DBG("path %s bridge %s", path, bridge);
-
if (!bridge) {
g_free(tethering);
return false;
return false;
}
+ DBG("path %s bridge %s", path, bridge);
+
tethering->technology = technology;
tethering->bridge = g_strdup(bridge);
tethering->enable = enabled;
if (index < 0)
goto out;
- info->address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
+ info->address = connman_ipaddress_alloc(AF_INET);
if (!info->address)
goto out;
return -errno;
vifr.cmd = GET_VLAN_VID_CMD;
- stpncpy(vifr.device1, ifname, sizeof(vifr.device1));
+ stpncpy(vifr.device1, ifname, sizeof(vifr.device1) - 1);
if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0)
vid = vifr.u.VID;
return -errno;
memset(&ifr, 0, sizeof(ifr));
- stpncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ stpncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
/* check if it is a vlan and get physical interface name*/
vifr.cmd = GET_VLAN_REALDEV_NAME_CMD;
- stpncpy(vifr.device1, ifname, sizeof(vifr.device1));
+ stpncpy(vifr.device1, ifname, sizeof(vifr.device1) - 1);
- if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0)
- stpncpy(ifr.ifr_name, vifr.u.device2, sizeof(ifr.ifr_name));
+ if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0) {
+ stpncpy(ifr.ifr_name, vifr.u.device2, sizeof(ifr.ifr_name) - 1);
+ ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
+ }
/* get driver info */
drvinfocmd.cmd = ETHTOOL_GDRVINFO;
struct iwd_device *iwdd;
struct connman_network *network;
+ /* service's autoconnect */
+ bool autoconnect;
};
struct iwd_known_network {
char *last_connected_time;
bool auto_connect;
int auto_connect_id;
+
+ /* service's autoconnect */
+ bool autoconnect;
};
struct iwd_station {
if (!strcmp(dbus_error, "net.connman.iwd.Failed"))
connman_network_set_error(iwdn->network,
CONNMAN_NETWORK_ERROR_INVALID_KEY);
- else
+ else if (!iwdn->autoconnect)
connman_network_set_error(iwdn->network,
CONNMAN_NETWORK_ERROR_CONNECT_FAIL);
return;
return -EINPROGRESS;
}
+struct auto_connect_cb_data {
+ char *path;
+ bool auto_connect;
+};
+
+static void auto_connect_cb_free(struct auto_connect_cb_data *cbd)
+{
+ g_free(cbd->path);
+ g_free(cbd);
+}
+
+static void auto_connect_cb(const DBusError *error, void *user_data)
+{
+ struct auto_connect_cb_data *cbd = user_data;
+ struct iwd_known_network *iwdkn;
+
+ iwdkn = g_hash_table_lookup(known_networks, cbd->path);
+ if (!iwdkn)
+ goto out;
+
+ if (dbus_error_is_set(error))
+ connman_warn("WiFi known network %s property auto connect %s",
+ cbd->path, error->message);
+
+ /* property is updated via watch known_network_property_change() */
+out:
+ auto_connect_cb_free(cbd);
+}
+
+static int set_auto_connect(struct iwd_known_network *iwdkn, bool auto_connect)
+{
+ dbus_bool_t dbus_auto_connect = auto_connect;
+ struct auto_connect_cb_data *cbd;
+
+ if (proxy_get_bool(iwdkn->proxy, "AutoConnect") == auto_connect)
+ return -EALREADY;
+
+ cbd = g_new(struct auto_connect_cb_data, 1);
+ cbd->path = g_strdup(iwdkn->path);
+ cbd->auto_connect = auto_connect;
+
+ if (!g_dbus_proxy_set_property_basic(iwdkn->proxy, "AutoConnect",
+ DBUS_TYPE_BOOLEAN,
+ &dbus_auto_connect,
+ auto_connect_cb, cbd, NULL)) {
+ auto_connect_cb_free(cbd);
+ return -EIO;
+ }
+
+ return -EINPROGRESS;
+}
+
+static gboolean disable_auto_connect_cb(gpointer data)
+{
+ char *path = data;
+ struct iwd_known_network *iwdkn;
+
+ iwdkn = g_hash_table_lookup(known_networks, path);
+ if (!iwdkn)
+ return FALSE;
+
+ if (set_auto_connect(iwdkn, false) != -EINPROGRESS)
+ connman_warn("Failed to disable auto connect");
+
+ iwdkn->auto_connect_id = 0;
+ return FALSE;
+}
+
+static int disable_auto_connect(struct iwd_known_network *iwdkn)
+{
+ if (iwdkn->auto_connect_id)
+ return -EBUSY;
+
+ iwdkn->auto_connect_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ 0,
+ disable_auto_connect_cb,
+ g_strdup(iwdkn->path),
+ g_free);
+ return 0;
+}
+
+static gboolean enable_auto_connect_cb(gpointer data)
+{
+ char *path = data;
+ struct iwd_known_network *iwdkn;
+
+ iwdkn = g_hash_table_lookup(known_networks, path);
+ if (!iwdkn)
+ return FALSE;
+
+ if (set_auto_connect(iwdkn, true) != -EINPROGRESS)
+ connman_warn("Failed to enable auto connect");
+
+ iwdkn->auto_connect_id = 0;
+ return FALSE;
+}
+
+static int enable_auto_connect(struct iwd_known_network *iwdkn)
+{
+ if (iwdkn->auto_connect_id)
+ return -EBUSY;
+
+ iwdkn->auto_connect_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ 0,
+ enable_auto_connect_cb,
+ g_strdup(iwdkn->path),
+ g_free);
+ return 0;
+}
+
+static int update_auto_connect(struct iwd_known_network *iwdkn)
+{
+ DBG("auto_connect %d autoconnect %d", iwdkn->auto_connect, iwdkn->autoconnect);
+
+ if (iwdkn->auto_connect == iwdkn->autoconnect)
+ return -EALREADY;
+
+ if (iwdkn->autoconnect)
+ return enable_auto_connect(iwdkn);
+ return disable_auto_connect(iwdkn);
+}
+
+static int cm_network_set_autoconnect(struct connman_network *network,
+ bool autoconnect)
+{
+ struct iwd_network *iwdn = connman_network_get_data(network);
+ struct iwd_known_network *iwdkn;
+
+ DBG("autoconnect %d", autoconnect);
+
+ iwdn->autoconnect = autoconnect;
+
+ if (!iwdn->known_network)
+ return -ENOENT;
+
+ iwdkn = g_hash_table_lookup(known_networks, iwdn->known_network);
+ if (!iwdkn)
+ return -ENOENT;
+
+ iwdkn->autoconnect = autoconnect;
+
+ return update_auto_connect(iwdkn);
+}
+
static struct connman_network_driver network_driver = {
- .name = "iwd",
- .type = CONNMAN_NETWORK_TYPE_WIFI,
- .probe = cm_network_probe,
- .connect = cm_network_connect,
- .disconnect = cm_network_disconnect,
+ .name = "iwd",
+ .type = CONNMAN_NETWORK_TYPE_WIFI,
+ .probe = cm_network_probe,
+ .connect = cm_network_connect,
+ .disconnect = cm_network_disconnect,
+ .set_autoconnect = cm_network_set_autoconnect,
};
static int cm_device_probe(struct connman_device *device)
{
struct tech_cb_data *cbd = user_data;
struct iwd_device *iwdd;
- struct iwd_ap *iwdap;
+ struct iwd_ap *iwdap = NULL;
DBG("");
if (!g_dbus_proxy_method_call(iwdap->proxy, "Stop",
NULL, tech_ap_stop_cb, cbd, NULL)) {
- connman_warn("iwd ap %s could not start AccessPoint mode: %s",
+ connman_warn("iwd ap %s could not stop AccessPoint mode: %s",
cbd->path, error->message);
goto out;
}
DBusMessageIter *iter, void *user_data)
{
struct iwd_network *iwdn;
+ struct iwd_known_network *iwdkn;
const char *path;
path = g_dbus_proxy_get_path(proxy);
update_network_connected(iwdn);
else
update_network_disconnected(iwdn);
+ } else if (!strcmp(name, "KnownNetwork")) {
+ g_free(iwdn->known_network);
+ iwdn->known_network =
+ g_strdup(proxy_get_string(proxy, "KnownNetwork"));
+ if (!iwdn->known_network)
+ return;
+
+ iwdkn = g_hash_table_lookup(known_networks,
+ iwdn->known_network);
+ if (iwdkn)
+ update_auto_connect(iwdkn);
}
}
connman_network_set_strength(iwdn->network,
calculate_strength(signal_strength));
+ connman_network_set_available(iwdn->network, true);
connman_network_update(iwdn->network);
}
static void ordered_networks_cb(DBusMessage *message, void *user_data)
{
DBusMessageIter array, entry;
+ struct iwd_device *iwdd;
+ char *path = user_data;
DBG("");
dbus_message_iter_next(&entry);
}
+
+ iwdd = g_hash_table_lookup(devices, path);
+ if (iwdd)
+ connman_device_set_scanning(iwdd->device,
+ CONNMAN_SERVICE_TYPE_WIFI, false);
}
static void update_signal_strength(struct iwd_station *iwds)
if (!g_dbus_proxy_method_call(iwds->proxy,
"GetOrderedNetworks",
NULL, ordered_networks_cb,
- NULL, NULL))
+ g_strdup(iwds->path), g_free))
DBG("GetOrderedNetworks() failed");
}
DBusMessageIter *iter, void *user_data)
{
struct iwd_station *iwds;
+ struct iwd_device *iwdd;
const char *path;
path = g_dbus_proxy_get_path(proxy);
dbus_message_iter_get_basic(iter, &scanning);
iwds->scanning = scanning;
- if (!iwds->scanning)
+ if (iwds->scanning) {
+ iwdd = g_hash_table_lookup(devices, path);
+ if (iwdd)
+ connman_device_set_scanning(iwdd->device,
+ CONNMAN_SERVICE_TYPE_WIFI, true);
+ } else {
update_signal_strength(iwds);
+ }
+
DBG("%s scanning %d", path, iwds->scanning);
}
add_network(path, iwdn);
}
-struct auto_connect_cb_data {
- char *path;
- bool auto_connect;
-};
-
-static void auto_connect_cb_free(struct auto_connect_cb_data *cbd)
-{
- g_free(cbd->path);
- g_free(cbd);
-}
-
-static void auto_connect_cb(const DBusError *error, void *user_data)
-{
- struct auto_connect_cb_data *cbd = user_data;
- struct iwd_known_network *iwdkn;
-
- iwdkn = g_hash_table_lookup(known_networks, cbd->path);
- if (!iwdkn)
- goto out;
-
- if (dbus_error_is_set(error))
- connman_warn("WiFi known network %s property auto connect %s",
- cbd->path, error->message);
-
- /* property is updated via watch known_network_property_change() */
-out:
- auto_connect_cb_free(cbd);
-}
-
-static int set_auto_connect(struct iwd_known_network *iwdkn, bool auto_connect)
-{
- dbus_bool_t dbus_auto_connect = auto_connect;
- struct auto_connect_cb_data *cbd;
-
- if (proxy_get_bool(iwdkn->proxy, "AutoConnect") == auto_connect)
- return -EALREADY;
-
- cbd = g_new(struct auto_connect_cb_data, 1);
- cbd->path = g_strdup(iwdkn->path);
- cbd->auto_connect = auto_connect;
-
- if (!g_dbus_proxy_set_property_basic(iwdkn->proxy, "AutoConnect",
- DBUS_TYPE_BOOLEAN,
- &dbus_auto_connect,
- auto_connect_cb, cbd, NULL)) {
- auto_connect_cb_free(cbd);
- return -EIO;
- }
-
- return -EINPROGRESS;
-}
-
-static gboolean disable_auto_connect_cb(gpointer data)
-{
- char *path = data;
- struct iwd_known_network *iwdkn;
-
- iwdkn = g_hash_table_lookup(known_networks, path);
- if (!iwdkn)
- return FALSE;
-
- if (set_auto_connect(iwdkn, false) != -EINPROGRESS)
- connman_warn("Failed to disable auto connect");
-
- iwdkn->auto_connect_id = 0;
- return FALSE;
-}
-
-static void disable_auto_connect(struct iwd_known_network *iwdkn)
-{
- if (iwdkn->auto_connect_id)
- return;
-
- iwdkn->auto_connect_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
- 0,
- disable_auto_connect_cb,
- g_strdup(iwdkn->path),
- g_free);
-}
-
static void known_network_property_change(GDBusProxy *proxy, const char *name,
DBusMessageIter *iter, void *user_data)
{
DBG("%p auto_connect %d", path, iwdkn->auto_connect);
- if (iwdkn->auto_connect)
- disable_auto_connect(iwdkn);
+ update_auto_connect(iwdkn);
+ }
+}
+
+static void init_auto_connect(struct iwd_known_network *iwdkn)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init(&iter, networks);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ struct iwd_network *iwdn = value;
+ struct iwd_known_network *kn;
+
+ if (!iwdn->known_network)
+ continue;
+
+ kn = g_hash_table_lookup(known_networks, iwdn->known_network);
+ if (iwdkn != kn)
+ continue;
+
+ iwdkn->autoconnect = iwdn->autoconnect;
+ update_auto_connect(iwdkn);
+ return;
}
}
iwdkn->name, iwdkn->type, iwdkn->hidden,
iwdkn->last_connected_time, iwdkn->auto_connect);
+ init_auto_connect(iwdkn);
+
g_dbus_proxy_set_property_watch(iwdkn->proxy,
known_network_property_change, NULL);
-
- if (iwdkn->auto_connect)
- disable_auto_connect(iwdkn);
}
static void create_station(GDBusProxy *proxy)
DBusMessage *reply;
if (!dbus_pending_call_get_completed(pending))
- return;
+ goto out;
register_call = NULL;
if (context->ipv4_method != CONNMAN_IPCONFIG_METHOD_FIXED)
goto out;
- context->ipv4_address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
+ context->ipv4_address = connman_ipaddress_alloc(AF_INET);
if (!context->ipv4_address) {
context->index = -1;
goto out;
context->ipv6_method = CONNMAN_IPCONFIG_METHOD_AUTO;
context->ipv6_address =
- connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV6);
+ connman_ipaddress_alloc(AF_INET6);
if (!context->ipv6_address)
goto out;
char *domain;
char **nameservers;
bool immutable;
+ bool default_route_set;
GHashTable *server_routes;
GHashTable *user_routes;
GResolv *resolv;
guint resolv_id;
+ guint remove_resolv_id;
};
static int set_string(struct connman_provider *provider,
return data->host_ip[0];
} else if (g_str_equal(key, "VPN.Domain"))
return data->domain;
+ else if (g_str_equal(key, "Transport"))
+ return data->service_ident;
return g_hash_table_lookup(data->setting_strings, key);
}
static void cancel_host_resolv(struct connection_data *data)
{
- if (data->resolv_id != 0)
+
+ if (data->remove_resolv_id)
+ g_source_remove(data->remove_resolv_id);
+
+ if (data->resolv && data->resolv_id)
g_resolv_cancel_lookup(data->resolv, data->resolv_id);
data->resolv_id = 0;
+ data->remove_resolv_id = 0;
g_resolv_unref(data->resolv);
data->resolv = NULL;
* We cannot unref the resolver here as resolv struct is manipulated
* by gresolv.c after we return from this callback.
*/
- g_idle_add(remove_resolv, data);
+ data->remove_resolv_id = g_idle_add(remove_resolv, data);
data->resolv_id = 0;
}
static void set_provider_state(struct connection_data *data)
{
enum connman_provider_state state = CONNMAN_PROVIDER_STATE_UNKNOWN;
+ bool connected;
int err = 0;
DBG("provider %p new state %s", data->provider, data->state);
- if (!provider_is_connected(data)) {
- g_free(data->service_ident);
- data->service_ident = NULL;
- }
+ connected = provider_is_connected(data);
if (g_str_equal(data->state, "ready")) {
state = CONNMAN_PROVIDER_STATE_READY;
}
connman_provider_set_state(data->provider, state);
- return;
+ goto free;
set:
if (data->cb_data)
free_config_cb_data(data->cb_data);
data->cb_data = NULL;
+
+free:
+ if (!connected) {
+ g_free(data->service_ident);
+ data->service_ident = NULL;
+ }
}
static int create_provider(struct connection_data *data, void *user_data)
}
connman_ipaddress_set_peer(data->ip, peer);
+ connman_ipaddress_set_p2p(data->ip, true);
return 0;
}
struct connection_data *data = user_data;
struct config_create_data *cb_data = data->cb_data;
- if (!dbus_pending_call_get_completed(call))
- return;
+ DBG("");
+
+ if (!dbus_pending_call_get_completed(call)) {
+ connman_warn("vpn connect reply pending call incomplete");
+ goto out;
+ }
if (call != data->call) {
connman_error("invalid call %p to VPN connect_reply data %p "
DBUS_DICT_ENTRY_END_CHAR_AS_STRING
DBUS_STRUCT_END_CHAR_AS_STRING;
- if (!dbus_pending_call_get_completed(call))
- return;
-
DBG("");
+ if (!dbus_pending_call_get_completed(call)) {
+ connman_warn("get connections reply pending call incomplete");
+ goto out;
+ }
+
reply = dbus_pending_call_steal_reply(call);
+ if (!reply)
+ goto out;
dbus_error_init(&error);
done:
dbus_message_unref(reply);
+out:
dbus_pending_call_unref(call);
}
DBusMessage *reply;
DBusError error;
- if (!dbus_pending_call_get_completed(call))
- return;
-
DBG("");
+ if (!dbus_pending_call_get_completed(call)) {
+ connman_warn("remove connection reply pending call incomplete");
+ goto out;
+ }
+
reply = dbus_pending_call_steal_reply(call);
+ if (!reply)
+ goto out;
dbus_error_init(&error);
dbus_message_unref(reply);
+out:
dbus_pending_call_unref(call);
}
dbus_pending_call_set_notify(data->disconnect_call, disconnect_reply,
data, NULL);
- g_free(data->service_ident);
- data->service_ident = NULL;
+ data->default_route_set = false;
connman_provider_set_state(data->provider,
CONNMAN_PROVIDER_STATE_DISCONNECT);
+
+ g_free(data->service_ident);
+ data->service_ident = NULL;
+
return -EINPROGRESS;
}
struct connection_data *data;
struct config_create_data *cb_data = user_data;
- if (!dbus_pending_call_get_completed(call))
- return;
-
DBG("user %p", cb_data);
+ if (!dbus_pending_call_get_completed(call)) {
+ connman_warn("configuration create reply pending call incomplete");
+ goto out;
+ }
+
reply = dbus_pending_call_steal_reply(call);
+ if (!reply)
+ goto out;
dbus_error_init(&error);
done:
dbus_message_unref(reply);
+out:
dbus_pending_call_unref(call);
}
return;
}
+ DBG("set route provider %p %s/%s/%s", data->provider,
+ route->network, route->gateway,
+ route->netmask);
+
+ /* Do not add default route for split routed VPNs.*/
+ if (connman_provider_is_split_routing(data->provider) &&
+ connman_inet_is_default_route(route->family,
+ route->network, route->gateway,
+ route->netmask))
+ return;
+
if (route->family == AF_INET6) {
unsigned char prefix_len = atoi(route->netmask);
route->gateway,
route->netmask);
}
+
+ if (connman_inet_is_default_route(route->family, route->network,
+ route->gateway, route->netmask))
+ data->default_route_set = true;
+}
+
+static int save_route(GHashTable *routes, int family, const char *network,
+ const char *netmask, const char *gateway);
+
+static int add_network_route(struct connection_data *data)
+{
+ struct vpn_route rt = { 0, };
+ int err;
+
+ if (!data)
+ return -EINVAL;
+
+ rt.family = connman_provider_get_family(data->provider);
+ switch (rt.family) {
+ case PF_INET:
+ err = connman_inet_get_route_addresses(data->index,
+ &rt.network, &rt.netmask, &rt.gateway);
+ break;
+ case PF_INET6:
+ err = connman_inet_ipv6_get_route_addresses(data->index,
+ &rt.network, &rt.netmask, &rt.gateway);
+ break;
+ default:
+ connman_error("invalid protocol family %d", rt.family);
+ return -EINVAL;
+ }
+
+ DBG("network %s gateway %s netmask %s for provider %p",
+ rt.network, rt.gateway, rt.netmask,
+ data->provider);
+
+ if (err) {
+ connman_error("cannot get network/gateway/netmask for %p",
+ data->provider);
+ goto out;
+ }
+
+ err = save_route(data->server_routes, rt.family, rt.network, rt.netmask,
+ rt.gateway);
+ if (err) {
+ connman_warn("failed to add network route for provider"
+ "%p", data->provider);
+ goto out;
+ }
+
+ set_route(data, &rt);
+
+out:
+ g_free(rt.network);
+ g_free(rt.netmask);
+ g_free(rt.gateway);
+
+ return 0;
+}
+
+static bool is_valid_route_table(struct connman_provider *provider,
+ GHashTable *table)
+{
+ GHashTableIter iter;
+ gpointer value, key;
+ struct vpn_route *route;
+ size_t table_size;
+
+ if (!table)
+ return false;
+
+ table_size = g_hash_table_size(table);
+
+ /* Non-split routed may have only the default route */
+ if (table_size > 0 && !connman_provider_is_split_routing(provider))
+ return true;
+
+ /* Split routed has more than the default route */
+ if (table_size > 1)
+ return true;
+
+ /*
+ * Only one route for split routed VPN, which should not be the
+ * default route.
+ */
+ g_hash_table_iter_init(&iter, table);
+ if (!g_hash_table_iter_next(&iter, &key, &value)) /* First and only */
+ return false;
+
+ route = value;
+ if (!route)
+ return false;
+
+ DBG("check route %d %s/%s/%s", route->family, route->network,
+ route->gateway, route->netmask);
+
+ if (!connman_inet_is_default_route(route->family, route->network,
+ route->gateway, route->netmask))
+ return true;
+
+ return false;
+}
+
+static bool check_routes(struct connman_provider *provider)
+{
+ struct connection_data *data;;
+
+ DBG("provider %p", provider);
+
+ data = connman_provider_get_data(provider);
+ if (!data)
+ return false;
+
+ if (is_valid_route_table(provider, data->user_routes))
+ return true;
+
+ if (is_valid_route_table(provider, data->server_routes))
+ return true;
+
+ return false;
}
static int set_routes(struct connman_provider *provider,
set_route(data, value);
}
- return 0;
-}
-
-static bool check_routes(struct connman_provider *provider)
-{
- struct connection_data *data;
-
- DBG("provider %p", provider);
+ /* If non-split routed VPN does not have a default route, add it */
+ if (!connman_provider_is_split_routing(provider) &&
+ !data->default_route_set) {
+ int family = connman_provider_get_family(provider);
+ const char *ipaddr_any = family == AF_INET6 ?
+ "::" : "0.0.0.0";
+ struct vpn_route def_route = {family, (char*) ipaddr_any,
+ (char*) ipaddr_any, NULL};
- data = connman_provider_get_data(provider);
- if (!data)
- return false;
-
- if (data->user_routes &&
- g_hash_table_size(data->user_routes) > 0)
- return true;
+ set_route(data, &def_route);
+ }
- if (data->server_routes &&
- g_hash_table_size(data->server_routes) > 0)
- return true;
+ /* Split routed VPN must have at least one route to the network */
+ if (connman_provider_is_split_routing(provider) &&
+ !check_routes(provider)) {
+ int err = add_network_route(data);
+ if (err) {
+ connman_warn("cannot add network route provider %p",
+ provider);
+ return err;
+ }
+ }
- return false;
+ return 0;
}
static struct connman_provider_driver provider_driver = {
route->gateway = g_strdup(gateway);
g_hash_table_replace(routes, key, route);
- } else
+ } else {
g_free(key);
+ return -EALREADY;
+ }
return 0;
}
+static void change_provider_split_routing(struct connman_provider *provider,
+ bool split_routing)
+{
+ struct connection_data *data;
+ int err;
+
+ if (!provider)
+ return;
+
+ if (connman_provider_is_split_routing(provider) == split_routing)
+ return;
+
+ data = connman_provider_get_data(provider);
+ if (split_routing && data && provider_is_connected(data) &&
+ !check_routes(provider)) {
+ err = add_network_route(data);
+ if (err) {
+ connman_warn("cannot add network route provider %p",
+ provider);
+ return;
+ }
+ }
+
+ err = connman_provider_set_split_routing(provider, split_routing);
+ switch (err) {
+ case 0:
+ /* fall through */
+ case -EALREADY:
+ break;
+ case -EINVAL:
+ /* fall through */
+ case -EOPNOTSUPP:
+ connman_warn("cannot change split routing %d", err);
+ default:
+ break;
+ }
+}
+
static int read_route_dict(GHashTable *routes, DBusMessageIter *dicts)
{
DBusMessageIter dict;
g_free(data->domain);
data->domain = g_strdup(str);
connman_provider_set_domain(data->provider, data->domain);
+ } else if (g_str_equal(key, "SplitRouting")) {
+ dbus_bool_t split_routing;
+ dbus_message_iter_get_basic(&value, &split_routing);
+ change_provider_split_routing(data->provider, split_routing);
}
if (ip_set && err == 0) {
if (!vpn_is_valid_transport(service)) {
connman_provider_disconnect(data->provider);
}
+
+ /* VPN moved to be split routed, default route is not set */
+ if (connman_provider_is_split_routing(data->provider))
+ data->default_route_set = false;
}
}
#include <connman/service.h>
#include <connman/peer.h>
#include <connman/log.h>
-#include <connman/option.h>
#include <connman/storage.h>
#include <include/setting.h>
#include <connman/provision.h>
#define BGSCAN_DEFAULT "simple:30:-65:300"
#define AUTOSCAN_EXPONENTIAL "exponential:3:300"
#define AUTOSCAN_SINGLE "single:3"
+#define SCAN_MAX_DURATION 10
#define P2P_FIND_TIMEOUT 30
#define P2P_CONNECTION_TIMEOUT 100
#define P2P_LISTEN_PERIOD 500
#define P2P_LISTEN_INTERVAL 2000
+#define ASSOC_STATUS_AUTH_TIMEOUT 16
#define ASSOC_STATUS_NO_CLIENT 17
#define LOAD_SHAPING_MAX_RETRIES 3
int assoc_code;
};
+struct disconnect_data {
+ struct wifi_data *wifi;
+ struct connman_network *network;
+};
+
static GList *iface_list = NULL;
static GList *pending_wifi_device = NULL;
if (version > 0) {
params->version = version;
- params->service = g_memdup(spec, spec_length);
+ if (spec_length > 0) {
+ params->service = g_malloc(spec_length);
+ memcpy(params->service, spec, spec_length);
+ }
} else if (query_length > 0 && spec_length > 0) {
- params->query = g_memdup(query, query_length);
+ params->query = g_malloc(query_length);
+ memcpy(params->query, query, query_length);
params->query_length = query_length;
- params->response = g_memdup(spec, spec_length);
+ params->response = g_malloc(spec_length);
+ memcpy(params->response, spec, spec_length);
params->response_length = spec_length;
} else {
- params->wfd_ies = g_memdup(spec, spec_length);
+ if (spec_length > 0) {
+ params->wfd_ies = g_malloc(spec_length);
+ memcpy(params->wfd_ies, spec, spec_length);
+ }
params->wfd_ies_length = spec_length;
}
}
if ((wifi->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
- if (flags & IFF_LOWER_UP) {
+ if (flags & IFF_LOWER_UP)
DBG("carrier on");
-
- handle_tethering(wifi);
- } else
+ else
DBG("carrier off");
}
+ if (flags & IFF_LOWER_UP)
+ handle_tethering(wifi);
+
wifi->flags = flags;
}
scan_params->num_ssids = i;
scan_params->ssids = g_slist_reverse(scan_params->ssids);
- scan_params->freqs = g_memdup(orig_params->freqs,
- sizeof(uint16_t) * orig_params->num_freqs);
- if (!scan_params->freqs)
+ if (orig_params->num_freqs <= 0)
goto err;
+ scan_params->freqs =
+ g_malloc(sizeof(uint16_t) * orig_params->num_freqs);
+ memcpy(scan_params->freqs, orig_params->freqs,
+ sizeof(uint16_t) *orig_params->num_freqs);
+
scan_params->num_freqs = orig_params->num_freqs;
} else
void *user_data)
{
struct wifi_data *wifi = user_data;
+ char *bgscan_range_max;
+ long value;
DBG("result %d ifname %s, wifi %p", result,
g_supplicant_interface_get_ifname(interface),
wifi->interface_ready = true;
finalize_interface_creation(wifi);
}
+
+ /*
+ * Set the BSS expiration age to match the long scanning
+ * interval to avoid the loss of unconnected networks between
+ * two scans.
+ */
+ bgscan_range_max = strrchr(BGSCAN_DEFAULT, ':');
+ if (!bgscan_range_max || strlen(bgscan_range_max) < 1)
+ return;
+
+ value = strtol(bgscan_range_max + 1, NULL, 10);
+ if (value <= 0 || errno == ERANGE)
+ return;
+
+ if (g_supplicant_interface_set_bss_expiration_age(interface,
+ value + SCAN_MAX_DURATION) < 0) {
+ connman_warn("Failed to set bss expiration age");
+ }
}
static int wifi_enable(struct connman_device *device)
struct wifi_data *wifi = connman_device_get_data(device);
int index;
char *interface;
- const char *driver = connman_option_get_string("wifi");
+ const char *driver = connman_setting_get_string("wifi");
int ret;
DBG("device %p %p", device, wifi);
static void disconnect_callback(int result, GSupplicantInterface *interface,
void *user_data)
{
- struct wifi_data *wifi = user_data;
+ struct disconnect_data *dd = user_data;
+ struct connman_network *network = dd->network;
+ struct wifi_data *wifi = dd->wifi;
- DBG("result %d supplicant interface %p wifi %p",
- result, interface, wifi);
+ g_free(dd);
+
+ DBG("result %d supplicant interface %p wifi %p networks: current %p "
+ "pending %p disconnected %p", result, interface, wifi,
+ wifi->network, wifi->pending_network, network);
if (result == -ECONNABORTED) {
DBG("wifi interface no longer available");
return;
}
- if (wifi->network && wifi->network != wifi->pending_network)
- connman_network_set_connected(wifi->network, false);
- wifi->network = NULL;
+ if (g_slist_find(wifi->networks, network))
+ connman_network_set_connected(network, false);
wifi->disconnecting = false;
+
+ if (network != wifi->network) {
+ if (network == wifi->pending_network)
+ wifi->pending_network = NULL;
+ DBG("current wifi network has changed since disconnection");
+ return;
+ }
+
+ wifi->network = NULL;
+
wifi->connected = false;
if (wifi->pending_network) {
static int network_disconnect(struct connman_network *network)
{
struct connman_device *device = connman_network_get_device(network);
+ struct disconnect_data *dd;
struct wifi_data *wifi;
int err;
wifi->disconnecting = true;
+ dd = g_malloc0(sizeof(*dd));
+ dd->wifi = wifi;
+ dd->network = network;
+
err = g_supplicant_interface_disconnect(wifi->interface,
- disconnect_callback, wifi);
- if (err < 0)
+ disconnect_callback, dd);
+ if (err < 0) {
wifi->disconnecting = false;
+ g_free(dd);
+ }
return err;
}
if (wps) {
const unsigned char *ssid, *wps_ssid;
unsigned int ssid_len, wps_ssid_len;
+ struct disconnect_data *dd;
const char *wps_key;
/* Checking if we got associated with requested
if (!wps_ssid || wps_ssid_len != ssid_len ||
memcmp(ssid, wps_ssid, ssid_len) != 0) {
+ dd = g_malloc0(sizeof(*dd));
+ dd->wifi = wifi;
+ dd->network = network;
+
connman_network_set_associating(network, false);
g_supplicant_interface_disconnect(wifi->interface,
- disconnect_callback, wifi);
+ disconnect_callback, dd);
return false;
}
{
struct connman_service *service;
- if (wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE)
+ if ((wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE) &&
+ !((wifi->state == G_SUPPLICANT_STATE_ASSOCIATING) &&
+ (wifi->assoc_code == ASSOC_STATUS_AUTH_TIMEOUT)))
return false;
if (wifi->connected)
/* See table 8-36 Reason codes in IEEE Std 802.11 */
switch (wifi->disconnect_code) {
- case 1: /* Unspecified reason */
- /* Let's assume it's because we got blocked */
-
case 6: /* Class 2 frame received from nonauthenticated STA */
connman_network_set_error(network,
CONNMAN_NETWORK_ERROR_BLOCKED);
void *user_data)
{
struct wifi_tethering_info *info = user_data;
- const char *driver = connman_option_get_string("wifi");
+ const char *driver = connman_setting_get_string("wifi");
DBG("ifname %s result %d ", info->ifname, result);
err = __connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
- ip_address, NULL, prefix_len, broadcast);
+ ip_address, NULL, prefix_len, broadcast,
+ false);
if (err < 0)
return err;
{
DBusMessage *reply;
DBusMessageIter array, dict;
+ dbus_bool_t is_synced;
struct timeval tv;
const char *str;
connman_dbus_dict_append_array(&dict, "Timeservers",
DBUS_TYPE_STRING, append_timeservers, NULL);
+ is_synced = __connman_timeserver_is_synced();
+ connman_dbus_dict_append_basic(&dict, "TimeserverSynced",
+ DBUS_TYPE_BOOLEAN, &is_synced);
+
connman_dbus_dict_close(&array, &dict);
return reply;
if (settimeofday(&tv, NULL) < 0)
return __connman_error_invalid_arguments(msg);
+ __connman_timeserver_set_synced(false);
connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
CONNMAN_CLOCK_INTERFACE, "Time",
DBUS_TYPE_UINT64, &newval);
connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
CONNMAN_CLOCK_INTERFACE, "TimeUpdates",
DBUS_TYPE_STRING, &strval);
+
+ if (newval == TIME_UPDATES_AUTO) {
+ struct connman_service *service;
+
+ service = connman_service_get_default();
+ __connman_timeserver_conf_update(service);
+ }
} else if (g_str_equal(name, "Timezone")) {
const char *strval;
connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
CONNMAN_CLOCK_INTERFACE, "Timeservers",
DBUS_TYPE_STRING, append_timeservers, NULL);
+ } else if (g_str_equal(name, "TimeserverSynced")) {
+ return __connman_error_permission_denied(msg);
} else
return __connman_error_invalid_property(msg);
return -1;
}
+int __connman_connection_get_vpn_phy_index(int vpn_index)
+{
+ GHashTableIter iter;
+ gpointer value, key;
+
+ g_hash_table_iter_init(&iter, gateway_hash);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ struct gateway_data *data = value;
+
+ if (data->index != vpn_index)
+ continue;
+
+ if (data->ipv4_gateway)
+ return data->ipv4_gateway->vpn_phy_index;
+
+ if (data->ipv6_gateway)
+ return data->ipv6_gateway->vpn_phy_index;
+ }
+
+ return -1;
+}
+
int __connman_connection_init(void)
{
int err;
#include <connman/backtrace.h>
-#include <connman/option.h>
-
#include <connman/setting.h>
#include <connman/plugin.h>
const char *address,
const char *peer,
unsigned char prefixlen,
- const char *broadcast);
+ const char *broadcast,
+ bool is_p2p);
int __connman_inet_get_interface_address(int index, int family, void *address);
int __connman_inet_get_interface_ll_address(int index, int family, void *address);
int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address);
char *peer;
char *broadcast;
char *gateway;
+ bool is_p2p; /* P2P connection or VPN, broadcast is excluded. */
};
struct connman_ipconfig_ops {
GSList *__connman_timeserver_add_list(GSList *server_list,
const char *timeserver);
GSList *__connman_timeserver_get_all(struct connman_service *service);
-int __connman_timeserver_sync(struct connman_service *service);
+void __connman_timeserver_sync(struct connman_service *service);
+void __connman_timeserver_conf_update(struct connman_service *service);
+
+bool __connman_timeserver_is_synced(void);
+void __connman_timeserver_set_synced(bool status);
enum __connman_dhcpv6_status {
CONNMAN_DHCPV6_STATUS_FAIL = 0,
void __connman_connection_gateway_remove(struct connman_service *service,
enum connman_ipconfig_type type);
int __connman_connection_get_vpn_index(int phy_index);
+int __connman_connection_get_vpn_phy_index(int vpn_index);
bool __connman_connection_update_gateway(void);
const char *__connman_network_get_group(struct connman_network *network);
const char *__connman_network_get_ident(struct connman_network *network);
bool __connman_network_get_weakness(struct connman_network *network);
+bool __connman_network_native_autoconnect(struct connman_network *network);
int __connman_config_init();
void __connman_config_cleanup(void);
bool __connman_provider_is_immutable(struct connman_provider *provider);
int __connman_provider_create_and_connect(DBusMessage *msg);
const char * __connman_provider_get_ident(struct connman_provider *provider);
+const char * __connman_provider_get_transport_ident(
+ struct connman_provider *provider);
int __connman_provider_indicate_state(struct connman_provider *provider,
enum connman_provider_state state);
int __connman_provider_indicate_error(struct connman_provider *provider,
int __connman_service_init(void);
void __connman_service_cleanup(void);
+int __connman_service_move(struct connman_service *service,
+ struct connman_service *target, bool before);
int __connman_service_load_modifiable(struct connman_service *service);
void __connman_service_list_struct(DBusMessageIter *iter);
void __connman_service_set_string(struct connman_service *service,
const char *key, const char *value);
-int __connman_service_online_check_failed(struct connman_service *service,
- enum connman_ipconfig_type type);
+void __connman_service_online_check(struct connman_service *service,
+ enum connman_ipconfig_type type,
+ bool success);
int __connman_service_ipconfig_indicate_state(struct connman_service *service,
enum connman_service_state new_state,
enum connman_ipconfig_type type);
int __connman_service_connect(struct connman_service *service,
enum connman_service_connect_reason reason);
int __connman_service_disconnect(struct connman_service *service);
-int __connman_service_disconnect_all(void);
void __connman_service_set_active_session(bool enable, GSList *list);
void __connman_service_auto_connect(enum connman_service_connect_reason reason);
bool __connman_service_remove(struct connman_service *service);
bool __connman_service_is_hidden(struct connman_service *service);
bool __connman_service_is_split_routing(struct connman_service *service);
bool __connman_service_index_is_split_routing(int index);
+void __connman_service_set_split_routing(struct connman_service *service,
+ bool split_routing);
+void __connman_service_split_routing_changed(struct connman_service *service);
int __connman_service_get_index(struct connman_service *service);
void __connman_service_set_hidden(struct connman_service *service);
void __connman_service_set_hostname(struct connman_service *service,
dbus_message_iter_init_append(signal, &iter);
connman_dbus_property_append_basic(&iter, key, type, val);
- g_dbus_send_message(connection, signal);
-
- return TRUE;
+ return g_dbus_send_message(connection, signal);
}
dbus_bool_t connman_dbus_property_changed_dict(const char *path,
dbus_message_iter_init_append(signal, &iter);
connman_dbus_property_append_dict(&iter, key, function, user_data);
- g_dbus_send_message(connection, signal);
-
- return TRUE;
+ return g_dbus_send_message(connection, signal);
}
dbus_bool_t connman_dbus_property_changed_array(const char *path,
connman_dbus_property_append_array(&iter, key, type,
function, user_data);
- g_dbus_send_message(connection, signal);
-
- return TRUE;
+ return g_dbus_send_message(connection, signal);
}
dbus_bool_t connman_dbus_setting_changed_basic(const char *owner,
connman_dbus_dict_close(&array, &dict);
- g_dbus_send_message(connection, msg);
-
- return TRUE;
+ return g_dbus_send_message(connection, msg);
}
dbus_bool_t connman_dbus_setting_changed_dict(const char *owner,
connman_dbus_dict_close(&array, &dict);
- g_dbus_send_message(connection, msg);
-
- return TRUE;
+ return g_dbus_send_message(connection, msg);
}
dbus_bool_t connman_dbus_setting_changed_array(const char *owner,
connman_dbus_dict_close(&array, &dict);
- g_dbus_send_message(connection, msg);
-
- return TRUE;
+ return g_dbus_send_message(connection, msg);
}
dbus_bool_t __connman_dbus_append_objpath_dict_array(DBusMessage *msg,
g_dhcp_client_set_request(dhcp_client, G_DHCP_ROUTER);
g_dhcp_client_set_request(dhcp_client, G_DHCP_SUBNET);
- vendor_class_id = connman_option_get_string("VendorClassID");
+ vendor_class_id = connman_setting_get_string("VendorClassID");
if (vendor_class_id)
g_dhcp_client_set_send(dhcp_client, G_DHCP_VENDOR_CLASS_ID,
vendor_class_id);
ref_own_address(user_data);
- if (inet_pton(AF_INET6, address, &addr) < 0) {
+ if (inet_pton(AF_INET6, address, &addr) != 1) {
DBG("Invalid IPv6 address %s %d/%s", address,
-errno, strerror(errno));
goto fail;
if (type == AF_INET) {
result = inet_pton(type, server, ipv4_bytes);
- if (!result) {
+ if (result != 1) {
DBG("Failed to parse IPv4 address: %s",
server);
return;
&byte_array);
} else if (type == AF_INET6) {
result = inet_pton(type, server, ipv6_bytes);
- if (!result) {
+ if (result != 1) {
DBG("Failed to parse IPv6 address: %s", server);
return;
}
char **uncompressed_ptr)
{
char *uptr = *uncompressed_ptr; /* position in result buffer */
+ char * const uncomp_end = uncompressed + uncomp_len - 1;
debug("count %d ptr %p end %p uptr %p", field_count, ptr, end, uptr);
* tmp buffer.
*/
- ulen = strlen(name);
- strncpy(uptr, name, uncomp_len - (uptr - uncompressed));
+ ulen = strlen(name) + 1;
+ if ((uptr + ulen) > uncomp_end)
+ goto out;
+ strncpy(uptr, name, ulen);
debug("pos %d ulen %d left %d name %s", pos, ulen,
- (int)(uncomp_len - (uptr - uncompressed)), uptr);
+ (int)(uncomp_end - (uptr + ulen)), uptr);
uptr += ulen;
- *uptr++ = '\0';
ptr += pos;
* We copy also the fixed portion of the result (type, class,
* ttl, address length and the address)
*/
+ if ((uptr + NS_RRFIXEDSZ) > uncomp_end) {
+ debug("uncompressed data too large for buffer");
+ goto out;
+ }
memcpy(uptr, ptr, NS_RRFIXEDSZ);
dns_type = uptr[0] << 8 | uptr[1];
} else if (dns_type == ns_t_a || dns_type == ns_t_aaaa) {
dlen = uptr[-2] << 8 | uptr[-1];
- if (ptr + dlen > end) {
+ if ((ptr + dlen) > end || (uptr + dlen) > uncomp_end) {
debug("data len %d too long", dlen);
goto out;
}
* refresh interval, retry interval, expiration
* limit and minimum ttl). They are 20 bytes long.
*/
+ if ((uptr + 20) > uncomp_end || (ptr + 20) > end) {
+ debug("soa record too long");
+ goto out;
+ }
memcpy(uptr, ptr, 20);
uptr += 20;
ptr += 20;
bool server_enabled = false;
GSList *list;
int index;
+ int vpn_index;
DBG("service %p", service);
if (index < 0)
return;
+ /*
+ * In case non-split-routed VPN is set as split routed the DNS servers
+ * the VPN must be enabled as well, when the transport becomes the
+ * default service.
+ */
+ vpn_index = __connman_connection_get_vpn_index(index);
+
for (list = server_list; list; list = list->next) {
struct server_data *data = list->data;
DBG("Enabling DNS server %s", data->server);
data->enabled = true;
server_enabled = true;
+ } else if (data->index == vpn_index) {
+ DBG("Enabling DNS server of VPN %s", data->server);
+ data->enabled = true;
} else {
DBG("Disabling DNS server %s", data->server);
data->enabled = false;
const char *address,
const char *peer,
unsigned char prefixlen,
- const char *broadcast)
+ const char *broadcast,
+ bool is_p2p)
{
uint8_t request[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
int sk, err;
DBG("cmd %#x flags %#x index %d family %d address %s peer %s "
- "prefixlen %hhu broadcast %s", cmd, flags, index, family,
- address, peer, prefixlen, broadcast);
+ "prefixlen %hhu broadcast %s p2p %s", cmd, flags, index,
+ family, address, peer, prefixlen, broadcast,
+ is_p2p ? "true" : "false");
if (!address)
return -EINVAL;
ifaddrmsg->ifa_index = index;
if (family == AF_INET) {
- if (inet_pton(AF_INET, address, &ipv4_addr) < 1)
+ if (inet_pton(AF_INET, address, &ipv4_addr) != 1)
return -1;
- if (broadcast)
- inet_pton(AF_INET, broadcast, &ipv4_bcast);
- else
- ipv4_bcast.s_addr = ipv4_addr.s_addr |
- htonl(0xfffffffflu >> prefixlen);
-
if (peer) {
- if (inet_pton(AF_INET, peer, &ipv4_dest) < 1)
+ if (inet_pton(AF_INET, peer, &ipv4_dest) != 1)
return -1;
err = __connman_inet_rtnl_addattr_l(header,
if (err < 0)
return err;
- err = __connman_inet_rtnl_addattr_l(header,
- sizeof(request),
- IFA_BROADCAST,
- &ipv4_bcast,
- sizeof(ipv4_bcast));
- if (err < 0)
- return err;
+ /*
+ * Broadcast address must not be added for P2P / VPN as
+ * getifaddrs() cannot interpret destination address.
+ */
+ if (!is_p2p) {
+ if (broadcast)
+ inet_pton(AF_INET, broadcast, &ipv4_bcast);
+ else
+ ipv4_bcast.s_addr = ipv4_addr.s_addr |
+ htonl(0xfffffffflu >> prefixlen);
+ err = __connman_inet_rtnl_addattr_l(header,
+ sizeof(request),
+ IFA_BROADCAST,
+ &ipv4_bcast,
+ sizeof(ipv4_bcast));
+ if (err < 0)
+ return err;
+ }
} else if (family == AF_INET6) {
- if (inet_pton(AF_INET6, address, &ipv6_addr) < 1)
+ if (inet_pton(AF_INET6, address, &ipv6_addr) != 1)
return -1;
err = __connman_inet_rtnl_addattr_l(header,
return err;
}
+static bool is_addr_unspec(int family, struct sockaddr *addr)
+{
+ struct sockaddr_in *in4;
+ struct sockaddr_in6 *in6;
+
+ switch (family) {
+ case AF_INET:
+ in4 = (struct sockaddr_in*) addr;
+ return in4->sin_addr.s_addr == INADDR_ANY;
+ case AF_INET6:
+ in6 = (struct sockaddr_in6*) addr;
+ return IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr);
+ default:
+ return false;
+ }
+}
+
+static bool is_addr_ll(int family, struct sockaddr *addr)
+{
+ struct sockaddr_in *in4;
+ struct sockaddr_in6 *in6;
+
+ switch (family) {
+ case AF_INET:
+ in4 = (struct sockaddr_in*) addr;
+ return (in4->sin_addr.s_addr & IN_CLASSB_NET) ==
+ ((in_addr_t) htonl(0xa9fe0000));
+ case AF_INET6:
+ in6 = (struct sockaddr_in6*) addr;
+ return IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr);
+ default:
+ return false;
+ }
+}
+
bool __connman_inet_is_any_addr(const char *address, int family)
{
bool ret = false;
struct addrinfo hints;
struct addrinfo *result = NULL;
- struct sockaddr_in6 *in6 = NULL;
- struct sockaddr_in *in4 = NULL;
if (!address || !*address)
goto out;
goto out;
if (result) {
- if (result->ai_family == AF_INET6) {
- in6 = (struct sockaddr_in6*)result->ai_addr;
- ret = IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr);
- } else if (result->ai_family == AF_INET) {
- in4 = (struct sockaddr_in*)result->ai_addr;
- ret = in4->sin_addr.s_addr == INADDR_ANY;
- }
-
+ ret = is_addr_unspec(result->ai_family, result->ai_addr);
freeaddrinfo(result);
}
int err;
unsigned char prefix_len;
const char *address;
+ bool is_p2p;
if (!ipaddress->local)
return 0;
prefix_len = ipaddress->prefixlen;
address = ipaddress->local;
+ is_p2p = ipaddress->is_p2p;
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
err = __connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, index, AF_INET6,
- address, NULL, prefix_len, NULL);
+ address, NULL, prefix_len, NULL, is_p2p);
if (err < 0) {
connman_error("%s: %s", __func__, strerror(-err));
return err;
int err;
unsigned char prefix_len;
const char *address, *broadcast, *peer;
+ bool is_p2p;
if (!ipaddress->local)
return -1;
address = ipaddress->local;
broadcast = ipaddress->broadcast;
peer = ipaddress->peer;
+ is_p2p = ipaddress->is_p2p;
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
err = __connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
- address, peer, prefix_len, broadcast);
+ address, peer, prefix_len, broadcast, is_p2p);
if (err < 0) {
connman_error("%s: %s", __func__, strerror(-err));
return err;
return 0;
}
-int connman_inet_clear_ipv6_address(int index, const char *address,
- int prefix_len)
+int connman_inet_clear_ipv6_address(int index,
+ struct connman_ipaddress *ipaddress)
{
int err;
+ int prefix_len;
+ const char *address;
+ bool is_p2p;
+
+ address = ipaddress->local;
+ prefix_len = ipaddress->prefixlen;
+ is_p2p = ipaddress->is_p2p;
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
return -EINVAL;
err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET6,
- address, NULL, prefix_len, NULL);
+ address, NULL, prefix_len, NULL, is_p2p);
if (err < 0) {
connman_error("%s: %s", __func__, strerror(-err));
return err;
int err;
unsigned char prefix_len;
const char *address, *broadcast, *peer;
+ bool is_p2p;
prefix_len = ipaddress->prefixlen;
address = ipaddress->local;
broadcast = ipaddress->broadcast;
peer = ipaddress->peer;
+ is_p2p = ipaddress->is_p2p;
DBG("index %d address %s prefix_len %d peer %s broadcast %s", index,
address, prefix_len, peer, broadcast);
return -EINVAL;
err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET,
- address, peer, prefix_len, broadcast);
+ address, peer, prefix_len, broadcast, is_p2p);
if (err < 0) {
connman_error("%s: %s", __func__, strerror(-err));
return err;
rt.rtmsg_dst_len = prefix_len;
- if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) {
+ if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) != 1) {
err = -errno;
goto out;
}
rt.rtmsg_dst_len = prefix_len;
- if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) {
+ if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) != 1) {
err = -errno;
goto out;
}
*/
if (gateway && !__connman_inet_is_any_addr(gateway, AF_INET6) &&
- inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) > 0)
+ inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) == 1)
rt.rtmsg_flags |= RTF_GATEWAY;
rt.rtmsg_metric = 1;
memset(&rt, 0, sizeof(rt));
- if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) < 0) {
+ if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) != 1) {
err = -errno;
goto out;
}
return err;
}
-bool connman_inet_compare_subnet(int index, const char *host)
+#define ADDR_TYPE_MAX 4
+
+struct interface_address {
+ int index;
+ int family;
+ bool allow_unspec;
+ /* Applies only to ADDR_TYPE_IPADDR in ipaddrs */
+ bool require_ll;
+ /* Real types must be in_addr for AF_INET and in6_addr for AF_INET6 */
+ void *ipaddrs[ADDR_TYPE_MAX];
+};
+
+enum ipaddr_type {
+ ADDR_TYPE_IPADDR = 0,
+ ADDR_TYPE_NETMASK,
+ ADDR_TYPE_BRDADDR,
+ ADDR_TYPE_DSTADDR
+};
+
+static int get_interface_addresses(struct interface_address *if_addr)
{
- struct ifreq ifr;
- struct in_addr _host_addr;
- in_addr_t host_addr, netmask_addr, if_addr;
- struct sockaddr_in *netmask, *addr;
- int sk;
+ struct ifaddrs *ifaddr;
+ struct ifaddrs *ifa;
+ struct sockaddr *addrs[ADDR_TYPE_MAX] = { 0 };
+ struct sockaddr_in *addr_in;
+ struct sockaddr_in6 *addr_in6;
+ char name[IF_NAMESIZE] = { 0 };
+ size_t len;
+ int err = -ENOENT;
+ int i;
- DBG("host %s", host);
+ if (!if_addr)
+ return -EINVAL;
- if (!host)
- return false;
+ if (!if_indextoname(if_addr->index, name))
+ return -EINVAL;
- if (inet_aton(host, &_host_addr) == 0)
- return false;
- host_addr = _host_addr.s_addr;
+ DBG("index %d interface %s", if_addr->index, name);
- sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return false;
+ if (getifaddrs(&ifaddr) < 0) {
+ connman_error("Cannot get addresses err %d/%s", errno,
+ strerror(errno));
+ return -errno;
+ }
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = index;
+ for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr)
+ continue;
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
- close(sk);
- return false;
+ if (g_strcmp0(ifa->ifa_name, name) ||
+ ifa->ifa_addr->sa_family !=
+ if_addr->family)
+ continue;
+
+
+ if (if_addr->ipaddrs[ADDR_TYPE_IPADDR]) {
+ if (!if_addr->allow_unspec && is_addr_unspec(
+ if_addr->family,
+ ifa->ifa_addr))
+ continue;
+
+ if (if_addr->require_ll && !is_addr_ll(if_addr->family,
+ ifa->ifa_addr))
+ continue;
+
+ addrs[ADDR_TYPE_IPADDR] = ifa->ifa_addr;
+ }
+
+ if (if_addr->ipaddrs[ADDR_TYPE_NETMASK]) {
+ if (!if_addr->allow_unspec && is_addr_unspec(
+ if_addr->family,
+ ifa->ifa_netmask))
+ continue;
+
+ addrs[ADDR_TYPE_NETMASK] = ifa->ifa_netmask;
+ }
+
+ if (if_addr->ipaddrs[ADDR_TYPE_BRDADDR] &&
+ (ifa->ifa_flags & IFF_BROADCAST)) {
+ if (!if_addr->allow_unspec && is_addr_unspec(
+ if_addr->family,
+ ifa->ifa_ifu.ifu_broadaddr))
+ continue;
+
+ addrs[ADDR_TYPE_BRDADDR] = ifa->ifa_ifu.ifu_broadaddr;
+ }
+
+ if (if_addr->ipaddrs[ADDR_TYPE_DSTADDR] &&
+ (ifa->ifa_flags & IFF_POINTOPOINT)) {
+ if (!if_addr->allow_unspec && is_addr_unspec(
+ if_addr->family,
+ ifa->ifa_ifu.ifu_dstaddr))
+ continue;
+
+ addrs[ADDR_TYPE_DSTADDR] = ifa->ifa_ifu.ifu_dstaddr;
+ }
+
+ err = 0;
+
+ break;
}
- if (ioctl(sk, SIOCGIFNETMASK, &ifr) < 0) {
- close(sk);
- return false;
+ if (err)
+ goto out;
+
+ for (i = 0; i < ADDR_TYPE_MAX; i++) {
+ if (!addrs[i])
+ continue;
+
+ switch (if_addr->family) {
+ case AF_INET:
+ len = sizeof(struct in_addr);
+ addr_in = (struct sockaddr_in*) addrs[i];
+ memcpy(if_addr->ipaddrs[i], &addr_in->sin_addr, len);
+ break;
+ case AF_INET6:
+ len = sizeof(struct in6_addr);
+ addr_in6 = (struct sockaddr_in6*) addrs[i];
+ memcpy(if_addr->ipaddrs[i], &addr_in6->sin6_addr, len);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
}
- netmask = (struct sockaddr_in *)&ifr.ifr_netmask;
- netmask_addr = netmask->sin_addr.s_addr;
+out:
+ freeifaddrs(ifaddr);
+ return err;
+}
- if (ioctl(sk, SIOCGIFADDR, &ifr) < 0) {
- close(sk);
+bool connman_inet_compare_subnet(int index, const char *host)
+{
+ struct interface_address if_addr = { 0 };
+ struct in_addr iaddr = { 0 };
+ struct in_addr imask = { 0 };
+ struct in_addr haddr = { 0 };
+
+ DBG("host %s", host);
+
+ if (!host)
return false;
- }
- close(sk);
+ if (inet_pton(AF_INET, host, &haddr) != 1)
+ return false;
+
+ if_addr.index = index;
+ if_addr.family = AF_INET;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &iaddr;
+ if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &imask;
- addr = (struct sockaddr_in *)&ifr.ifr_addr;
- if_addr = addr->sin_addr.s_addr;
+ if (get_interface_addresses(&if_addr))
+ return false;
- return ((if_addr & netmask_addr) == (host_addr & netmask_addr));
+ return (iaddr.s_addr & imask.s_addr) == (haddr.s_addr & imask.s_addr);
}
static bool mem_mask_equal(const void *a, const void *b,
bool connman_inet_compare_ipv6_subnet(int index, const char *host)
{
- struct ifaddrs *ifaddr, *ifa;
- bool rv = false;
- char name[IF_NAMESIZE];
- struct in6_addr haddr;
+ struct interface_address addr = { 0 };
+ struct in6_addr iaddr = { 0 };
+ struct in6_addr imask = { 0 };
+ struct in6_addr haddr = { 0 };
- if (inet_pton(AF_INET6, host, &haddr) <= 0)
+ if (inet_pton(AF_INET6, host, &haddr) != 1)
return false;
- if (!if_indextoname(index, name))
- return false;
+ addr.index = index;
+ addr.family = AF_INET6;
+ addr.ipaddrs[ADDR_TYPE_IPADDR] = &iaddr;
+ addr.ipaddrs[ADDR_TYPE_NETMASK] = &imask;
- DBG("index %d interface %s", index, name);
-
- if (getifaddrs(&ifaddr) < 0) {
- DBG("Cannot get addresses err %d/%s", errno, strerror(errno));
+ if (get_interface_addresses(&addr))
return false;
- }
-
- for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
- struct sockaddr_in6 *iaddr;
- struct sockaddr_in6 *imask;
-
- if (!ifa->ifa_addr)
- continue;
-
- if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) != 0 ||
- ifa->ifa_addr->sa_family != AF_INET6)
- continue;
-
- iaddr = (struct sockaddr_in6 *)ifa->ifa_addr;
- imask = (struct sockaddr_in6 *)ifa->ifa_netmask;
-
- rv = mem_mask_equal(&iaddr->sin6_addr, &haddr,
- &imask->sin6_addr,
- sizeof(haddr));
- goto out;
- }
-out:
- freeifaddrs(ifaddr);
- return rv;
+ return mem_mask_equal(&iaddr, &haddr, &imask, sizeof(haddr));
}
int connman_inet_remove_from_bridge(int index, const char *bridge)
return prefixes;
}
-static int get_dest_addr(int family, int index, char *buf, int len)
+int connman_inet_get_dest_addr(int index, char **dest)
{
- struct ifreq ifr;
- void *addr;
- int sk;
+ struct interface_address if_addr = { 0 };
+ struct in_addr dstaddr = { 0 };
+ char buf[INET_ADDRSTRLEN] = { 0 };
+ int err;
- sk = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return -errno;
+ if (!dest)
+ return -EINVAL;
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = index;
+ if_addr.index = index;
+ if_addr.family = AF_INET;
+ if_addr.allow_unspec = true;
+ if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dstaddr;
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
- DBG("SIOCGIFNAME (%d/%s)", errno, strerror(errno));
- close(sk);
- return -errno;
- }
+ err = get_interface_addresses(&if_addr);
+ if (err)
+ return err;
- if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
- DBG("SIOCGIFFLAGS (%d/%s)", errno, strerror(errno));
- close(sk);
- return -errno;
- }
+ if (inet_ntop(AF_INET, &dstaddr, buf, INET_ADDRSTRLEN))
+ *dest = g_strdup(buf);
- if ((ifr.ifr_flags & IFF_POINTOPOINT) == 0) {
- close(sk);
- errno = EINVAL;
- return -errno;
- }
+ DBG("destination %s", *dest);
- DBG("index %d %s", index, ifr.ifr_name);
+ return *dest && **dest ? 0 : -ENOENT;
+}
- if (ioctl(sk, SIOCGIFDSTADDR, &ifr) < 0) {
- connman_error("Get destination address failed (%s)",
- strerror(errno));
- close(sk);
- return -errno;
- }
+int connman_inet_ipv6_get_dest_addr(int index, char **dest)
+{
+ struct interface_address if_addr = { 0 };
+ struct in_addr dstaddr = { 0 };
+ char buf[INET6_ADDRSTRLEN] = { 0 };
+ int err;
- close(sk);
+ if (!dest)
+ return -EINVAL;
- switch (family) {
- case AF_INET:
- addr = &((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr;
- break;
- case AF_INET6:
- addr = &((struct sockaddr_in6 *)&ifr.ifr_dstaddr)->sin6_addr;
- break;
- default:
- errno = EINVAL;
- return -errno;
- }
+ if_addr.index = index;
+ if_addr.family = AF_INET6;
+ if_addr.allow_unspec = true;
+ if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dstaddr;
- if (!inet_ntop(family, addr, buf, len)) {
- DBG("error %d/%s", errno, strerror(errno));
- return -errno;
- }
+ err = get_interface_addresses(&if_addr);
+ if (err)
+ return err;
- return 0;
+ if (inet_ntop(AF_INET6, &dstaddr, buf, INET6_ADDRSTRLEN))
+ *dest = g_strdup(buf);
+
+ DBG("destination %s", *dest);
+
+ return *dest && **dest ? 0 : -ENOENT;
}
-int connman_inet_get_dest_addr(int index, char **dest)
+/* destination is optional */
+int connman_inet_get_route_addresses(int index, char **network, char **netmask,
+ char **destination)
{
- char addr[INET_ADDRSTRLEN];
- int ret;
+ struct interface_address if_addr = { 0 };
+ struct in_addr addr = { 0 };
+ struct in_addr mask = { 0 };
+ struct in_addr dest = { 0 };
+ struct in_addr nw_addr = { 0 };
+ char buf[INET_ADDRSTRLEN] = { 0 };
+ int err;
- ret = get_dest_addr(PF_INET, index, addr, INET_ADDRSTRLEN);
- if (ret < 0)
- return ret;
+ if (!network || !netmask)
+ return -EINVAL;
- *dest = g_strdup(addr);
+ if_addr.index = index;
+ if_addr.family = AF_INET;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &addr;
+ if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &mask;
+ if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dest;
- DBG("destination %s", *dest);
+ err = get_interface_addresses(&if_addr);
+ if (err)
+ return err;
- return 0;
+ nw_addr.s_addr = (addr.s_addr & mask.s_addr);
+
+ if (inet_ntop(AF_INET, &nw_addr, buf, INET_ADDRSTRLEN))
+ *network = g_strdup(buf);
+
+ memset(&buf, 0, INET_ADDRSTRLEN);
+
+ if (inet_ntop(AF_INET, &mask, buf, INET_ADDRSTRLEN))
+ *netmask = g_strdup(buf);
+
+ if (destination) {
+ memset(&buf, 0, INET_ADDRSTRLEN);
+
+ if (inet_ntop(AF_INET, &dest, buf, INET_ADDRSTRLEN))
+ *destination = g_strdup(buf);
+ }
+
+ DBG("network %s netmask %s destination %s", *network, *netmask,
+ destination ? *destination : NULL);
+
+ return *network && **network && *netmask && **netmask ? 0 : -ENOENT;
}
-int connman_inet_ipv6_get_dest_addr(int index, char **dest)
+int connman_inet_ipv6_get_route_addresses(int index, char **network,
+ char **netmask, char **destination)
{
- char addr[INET6_ADDRSTRLEN];
- int ret;
+ struct interface_address if_addr = { 0 };
+ struct in6_addr addr = { 0 };
+ struct in6_addr mask = { 0 };
+ struct in6_addr dest = { 0 };
+ struct in6_addr nw_addr = { 0 };
+ char buf[INET6_ADDRSTRLEN] = { 0 };
+ int err;
- ret = get_dest_addr(PF_INET6, index, addr, INET6_ADDRSTRLEN);
- if (ret < 0)
- return ret;
+ if (!network)
+ return -EINVAL;
- *dest = g_strdup(addr);
+ if_addr.index = index;
+ if_addr.family = AF_INET6;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &addr;
+ if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &mask;
+ if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dest;
- DBG("destination %s", *dest);
+ err = get_interface_addresses(&if_addr);
+ if (err)
+ return err;
- return 0;
+ ipv6_addr_set(&nw_addr, addr.s6_addr32[0] & mask.s6_addr32[0],
+ addr.s6_addr32[1] & mask.s6_addr32[1],
+ addr.s6_addr32[2] & mask.s6_addr32[2],
+ addr.s6_addr32[3] & mask.s6_addr32[3]);
+
+ if (inet_ntop(AF_INET6, &nw_addr, buf, INET6_ADDRSTRLEN))
+ *network = g_strdup(buf);
+
+ memset(&buf, 0, INET6_ADDRSTRLEN);
+
+ if (inet_ntop(AF_INET6, &mask, buf, INET6_ADDRSTRLEN))
+ *netmask = g_strdup(buf);
+
+ if (destination) {
+ memset(&buf, 0, INET6_ADDRSTRLEN);
+
+ if (inet_ntop(AF_INET6, &dest, buf, INET6_ADDRSTRLEN))
+ *destination = g_strdup(buf);
+ }
+
+ DBG("network %s netmask %s destination %s", *network, *netmask,
+ destination ? *destination : NULL);
+
+ return *network && **network && *netmask && **netmask ? 0 : -ENOENT;
}
int __connman_inet_rtnl_open(struct __connman_inet_rtnl_handle *rth)
return true;
}
-int __connman_inet_get_interface_address(int index, int family, void *address)
+/*
+ * Omits checking of the gateway matching the actual gateway IP since both
+ * connmand and vpnd use inet.c, getting the route is via ipconfig and ipconfig
+ * is different for both. Gateway is left here for possible future use.
+ *
+ * Gateway can be NULL and connection.c then assigns 0.0.0.0 address or ::,
+ * depending on IP family.
+ */
+bool connman_inet_is_default_route(int family, const char *host,
+ const char *gateway, const char *netmask)
{
- struct ifaddrs *ifaddr, *ifa;
- int err = -ENOENT;
- char name[IF_NAMESIZE];
-
- if (!if_indextoname(index, name))
- return -EINVAL;
-
- DBG("index %d interface %s", index, name);
-
- if (getifaddrs(&ifaddr) < 0) {
- err = -errno;
- DBG("Cannot get addresses err %d/%s", err, strerror(-err));
- return err;
- }
-
- for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
- if (!ifa->ifa_addr)
- continue;
-
- if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 &&
- ifa->ifa_addr->sa_family == family) {
- if (family == AF_INET) {
- struct sockaddr_in *in4 = (struct sockaddr_in *)
- ifa->ifa_addr;
- if (in4->sin_addr.s_addr == INADDR_ANY)
- continue;
- memcpy(address, &in4->sin_addr,
- sizeof(struct in_addr));
- } else if (family == AF_INET6) {
- struct sockaddr_in6 *in6 =
- (struct sockaddr_in6 *)ifa->ifa_addr;
- if (memcmp(&in6->sin6_addr, &in6addr_any,
- sizeof(struct in6_addr)) == 0)
- continue;
- memcpy(address, &in6->sin6_addr,
- sizeof(struct in6_addr));
+ return __connman_inet_is_any_addr(host, family) &&
+ __connman_inet_is_any_addr(netmask, family);
+}
- } else {
- err = -EINVAL;
- goto out;
- }
+int __connman_inet_get_interface_address(int index, int family, void *address)
+{
+ struct interface_address if_addr = { 0 };
- err = 0;
- break;
- }
- }
+ if_addr.index = index;
+ if_addr.family = family;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = address;
-out:
- freeifaddrs(ifaddr);
- return err;
+ return get_interface_addresses(&if_addr);
}
int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address)
ret = inet_pton(family, dst ? dst : gateway, buf);
g_free(dst);
- if (ret <= 0)
+ if (ret != 1)
return -EINVAL;
memset(&rth, 0, sizeof(rth));
int __connman_inet_get_interface_ll_address(int index, int family,
void *address)
{
- struct ifaddrs *ifaddr, *ifa;
- int err = -ENOENT;
- char name[IF_NAMESIZE];
-
- if (!if_indextoname(index, name))
- return -EINVAL;
-
- DBG("index %d interface %s", index, name);
+ struct interface_address if_addr = { 0 };
- if (getifaddrs(&ifaddr) < 0) {
- err = -errno;
- DBG("Cannot get addresses err %d/%s", err, strerror(-err));
- return err;
- }
+ if_addr.index = index;
+ if_addr.family = family;
+ if_addr.require_ll = true;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = address;
- for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
- if (!ifa->ifa_addr)
- continue;
-
- if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 &&
- ifa->ifa_addr->sa_family == family) {
- if (family == AF_INET) {
- struct sockaddr_in *in4 = (struct sockaddr_in *)
- ifa->ifa_addr;
- if (in4->sin_addr.s_addr == INADDR_ANY)
- continue;
- if ((in4->sin_addr.s_addr & IN_CLASSB_NET) !=
- ((in_addr_t) 0xa9fe0000))
- continue;
- memcpy(address, &in4->sin_addr,
- sizeof(struct in_addr));
- } else if (family == AF_INET6) {
- struct sockaddr_in6 *in6 =
- (struct sockaddr_in6 *)ifa->ifa_addr;
- if (memcmp(&in6->sin6_addr, &in6addr_any,
- sizeof(struct in6_addr)) == 0)
- continue;
- if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr))
- continue;
-
- memcpy(address, &in6->sin6_addr,
- sizeof(struct in6_addr));
- } else {
- err = -EINVAL;
- goto out;
- }
-
- err = 0;
- break;
- }
- }
-
-out:
- freeifaddrs(ifaddr);
- return err;
+ return get_interface_addresses(&if_addr);
}
int __connman_inet_get_address_netmask(int ifindex,
addrstr[len] = '\0';
err = inet_pton(AF_INET, addrstr, addr);
- if (err <= 0) {
+ if (err != 1) {
connman_error("%s: Cannot convert to numeric addr \"%s\"\n",
__func__, addrstr);
err = -1;
ipaddress->peer = NULL;
ipaddress->broadcast = NULL;
ipaddress->gateway = NULL;
+ ipaddress->is_p2p = false;
return ipaddress;
}
+void connman_ipaddress_set_p2p(struct connman_ipaddress *ipaddress, bool value)
+{
+ if (!ipaddress)
+ return;
+
+ ipaddress->is_p2p = value;
+}
+
void connman_ipaddress_free(struct connman_ipaddress *ipaddress)
{
if (!ipaddress)
return false;
err = inet_pton(AF_INET6, address, buf);
- if (err > 0)
+ if (err == 1)
return true;
return false;
copy->peer = g_strdup(ipaddress->peer);
copy->broadcast = g_strdup(ipaddress->broadcast);
copy->gateway = g_strdup(ipaddress->gateway);
+ copy->is_p2p = ipaddress->is_p2p;
return copy;
}
return "";
}
-static bool get_ipv6_state(gchar *ifname)
+#define PROC_IPV4_CONF_PREFIX "/proc/sys/net/ipv4/conf"
+#define PROC_IPV6_CONF_PREFIX "/proc/sys/net/ipv6/conf"
+
+static int read_conf_value(const char *prefix, const char *ifname,
+ const char *suffix, int *value)
{
- int disabled;
gchar *path;
FILE *f;
- bool enabled = false;
-
- if (!ifname)
- path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6");
- else
- path = g_strdup_printf(
- "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname);
+ int err;
+ path = g_build_filename(prefix, ifname ? ifname : "all", suffix, NULL);
if (!path)
- return enabled;
+ return -ENOMEM;
+ errno = 0;
f = fopen(path, "r");
+ if (!f) {
+ err = -errno;
+ } else {
+ errno = 0; /* Avoid stale errno values with fscanf */
- g_free(path);
+ err = fscanf(f, "%d", value);
+ if (err <= 0 && errno)
+ err = -errno;
- if (f) {
- if (fscanf(f, "%d", &disabled) > 0)
- enabled = !disabled;
fclose(f);
}
- return enabled;
+ if (err <= 0)
+ connman_error("failed to read %s", path);
+
+ g_free(path);
+
+ return err;
+}
+
+static int read_ipv4_conf_value(const char *ifname, const char *suffix,
+ int *value)
+{
+ return read_conf_value(PROC_IPV4_CONF_PREFIX, ifname, suffix, value);
}
-static void set_ipv6_state(gchar *ifname, bool enable)
+static int read_ipv6_conf_value(const char *ifname, const char *suffix,
+ int *value)
{
+ return read_conf_value(PROC_IPV6_CONF_PREFIX, ifname, suffix, value);
+}
+
+static int write_conf_value(const char *prefix, const char *ifname,
+ const char *suffix, int value) {
gchar *path;
FILE *f;
+ int rval;
- if (!ifname)
- path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6");
- else
- path = g_strdup_printf(
- "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname);
-
+ path = g_build_filename(prefix, ifname ? ifname : "all", suffix, NULL);
if (!path)
- return;
+ return -ENOMEM;
f = fopen(path, "r+");
+ if (!f) {
+ rval = -errno;
+ } else {
+ rval = fprintf(f, "%d", value);
+ fclose(f);
+ }
+
+ if (rval <= 0)
+ connman_error("failed to set %s value %d", path, value);
g_free(path);
- if (!f)
- return;
+ return rval;
+}
- if (!enable)
- fprintf(f, "1");
- else
- fprintf(f, "0");
+static int write_ipv4_conf_value(const char *ifname, const char *suffix,
+ int value)
+{
+ return write_conf_value(PROC_IPV4_CONF_PREFIX, ifname, suffix, value);
+}
- fclose(f);
+static int write_ipv6_conf_value(const char *ifname, const char *suffix,
+ int value)
+{
+ return write_conf_value(PROC_IPV6_CONF_PREFIX, ifname, suffix, value);
}
-static int get_ipv6_privacy(gchar *ifname)
+static bool get_ipv6_state(gchar *ifname)
{
- gchar *path;
- FILE *f;
- int value;
+ int disabled;
+ bool enabled = false;
- if (!ifname)
- return 0;
+ if (read_ipv6_conf_value(ifname, "disable_ipv6", &disabled) > 0)
+ enabled = !disabled;
- path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr",
- ifname);
+ return enabled;
+}
- if (!path)
- return 0;
+static int set_ipv6_state(gchar *ifname, bool enable)
+{
+ int disabled = enable ? 0 : 1;
- f = fopen(path, "r");
+ DBG("%s %d", ifname, disabled);
- g_free(path);
+ return write_ipv6_conf_value(ifname, "disable_ipv6", disabled);
+}
- if (!f)
+static int get_ipv6_privacy(gchar *ifname)
+{
+ int value;
+
+ if (!ifname)
return 0;
- if (fscanf(f, "%d", &value) <= 0)
+ if (read_ipv6_conf_value(ifname, "use_tempaddr", &value) < 0)
value = 0;
- fclose(f);
-
return value;
}
/* Enable the IPv6 privacy extension for stateless address autoconfiguration.
* The privacy extension is described in RFC 3041 and RFC 4941
*/
-static void set_ipv6_privacy(gchar *ifname, int value)
+static int set_ipv6_privacy(gchar *ifname, int value)
{
- gchar *path;
- FILE *f;
-
if (!ifname)
- return;
-
- path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr",
- ifname);
-
- if (!path)
- return;
+ return -EINVAL;
if (value < 0)
value = 0;
- f = fopen(path, "r+");
-
- g_free(path);
-
- if (!f)
- return;
-
- fprintf(f, "%d", value);
- fclose(f);
+ return write_ipv6_conf_value(ifname, "use_tempaddr", value);
}
static int get_rp_filter(void)
{
- FILE *f;
- int value = -EINVAL, tmp;
+ int value;
- f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r");
-
- if (f) {
- if (fscanf(f, "%d", &tmp) == 1)
- value = tmp;
- fclose(f);
- }
+ if (read_ipv4_conf_value(NULL, "rp_filter", &value) < 0)
+ value = -EINVAL;
return value;
}
-static void set_rp_filter(int value)
+static int set_rp_filter(int value)
{
- FILE *f;
-
- f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r+");
-
- if (!f)
- return;
-
- fprintf(f, "%d", value);
+ /* 0 = no validation, 1 = strict mode, 2 = loose mode */
+ switch (value) {
+ case -1:
+ value = 0;
+ /* fall through */
+ case 0:
+ case 1:
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
- fclose(f);
+ return write_ipv4_conf_value(NULL, "rp_filter", value);
}
int __connman_ipconfig_set_rp_filter()
return g_strcmp0(addr1->local, addr2->local);
}
+static bool is_index_p2p_service(int index)
+{
+ struct connman_service *service;
+ enum connman_service_type type;
+
+ service = __connman_service_lookup_from_index(index);
+ if (!service)
+ return false;
+
+ type = connman_service_get_type(service);
+ switch (type) {
+ case CONNMAN_SERVICE_TYPE_P2P:
+ case CONNMAN_SERVICE_TYPE_VPN:
+ return true;
+ default:
+ return false;
+ }
+}
+
int __connman_ipconfig_newaddr(int index, int family, const char *label,
unsigned char prefixlen, const char *address)
{
ipaddress->prefixlen = prefixlen;
ipaddress->local = g_strdup(address);
+ if (is_index_p2p_service(index))
+ connman_ipaddress_set_p2p(ipaddress, true);
+
if (g_slist_find_custom(ipdevice->address_list, ipaddress,
check_duplicate_address)) {
connman_ipaddress_free(ipaddress);
ipconfig->address->prefixlen = prefixlen;
}
+static void ipconfig_set_p2p(int index, struct connman_ipconfig *ipconfig)
+{
+ if (!is_index_p2p_service(index))
+ return;
+
+ connman_ipaddress_set_p2p(ipconfig->address, true);
+ connman_ipaddress_set_p2p(ipconfig->system, true);
+}
+
static struct connman_ipconfig *create_ipv6config(int index)
{
struct connman_ipconfig *ipv6config;
ipv6config->system = connman_ipaddress_alloc(AF_INET6);
+ ipconfig_set_p2p(index, ipv6config);
+
DBG("ipconfig %p index %d method %s", ipv6config, index,
__connman_ipconfig_method2string(ipv6config->method));
ipconfig->system = connman_ipaddress_alloc(AF_INET);
+ ipconfig_set_p2p(index, ipconfig);
+
DBG("ipconfig %p index %d", ipconfig, index);
return ipconfig;
err = connman_inet_clear_address(ipconfig->index,
ipconfig->address);
else if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6)
- err = connman_inet_clear_ipv6_address(
- ipconfig->index,
- ipconfig->address->local,
- ipconfig->address->prefixlen);
+ err = connman_inet_clear_ipv6_address(ipconfig->index,
+ ipconfig->address);
else
err = -EINVAL;
connman_ipaddress_clear(ipdevice->config_ipv4->system);
__connman_ipconfig_unref(ipdevice->config_ipv4);
+
+ g_free(ipdevice->ipv4_gateway);
+ ipdevice->ipv4_gateway = NULL;
}
if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
connman_ipaddress_clear(ipdevice->config_ipv6->system);
__connman_ipconfig_unref(ipdevice->config_ipv6);
+
+ g_free(ipdevice->ipv6_gateway);
+ ipdevice->ipv6_gateway = NULL;
}
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
connman_ipaddress_clear(ipdevice->config_ipv4->system);
__connman_ipconfig_unref(ipdevice->config_ipv4);
ipdevice->config_ipv4 = NULL;
+
+ g_free(ipdevice->ipv4_gateway);
+ ipdevice->ipv4_gateway = NULL;
+
return 0;
}
connman_ipaddress_clear(ipdevice->config_ipv6->system);
__connman_ipconfig_unref(ipdevice->config_ipv6);
ipdevice->config_ipv6 = NULL;
+
+ g_free(ipdevice->ipv6_gateway);
+ ipdevice->ipv6_gateway = NULL;
+
return 0;
}
if (!tokens)
return -1;
- if (!inet_pton(AF_INET, tokens[0], ip)) {
+ if (inet_pton(AF_INET, tokens[0], ip) != 1) {
err = -1;
goto out;
}
if (!tokens)
return -1;
- if (!inet_pton(AF_INET6, tokens[0], ip)) {
+ if (inet_pton(AF_INET6, tokens[0], ip) != 1) {
err = -1;
goto out;
}
break;
if (invert)
- ctx->ip->invflags |= IP6T_INV_DSTIP;
+ ctx->ipv6->invflags |= IP6T_INV_DSTIP;
}
break;
#define DEFAULT_INPUT_REQUEST_TIMEOUT (120 * 1000)
#define DEFAULT_BROWSER_LAUNCH_TIMEOUT (300 * 1000)
+/*
+ * We set the integer to 1 sec so that we have a chance to get
+ * necessary IPv6 router advertisement messages that might have
+ * DNS data etc.
+ */
+#define DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL 1
+#define DEFAULT_ONLINE_CHECK_MAX_INTERVAL 12
+
#define MAINFILE "main.conf"
#define CONFIGMAINFILE CONFIGDIR "/" MAINFILE
bool enable_6to4;
char *vendor_class_id;
bool enable_online_check;
+ bool enable_online_to_ready_transition;
+ unsigned int online_check_initial_interval;
+ unsigned int online_check_max_interval;
bool auto_connect_roaming_services;
bool acd;
bool use_gateways_as_timeservers;
.enable_6to4 = false,
.vendor_class_id = NULL,
.enable_online_check = true,
+ .enable_online_to_ready_transition = false,
+ .online_check_initial_interval = DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL,
+ .online_check_max_interval = DEFAULT_ONLINE_CHECK_MAX_INTERVAL,
.auto_connect_roaming_services = false,
.acd = false,
.use_gateways_as_timeservers = false,
#define CONF_ENABLE_6TO4 "Enable6to4"
#define CONF_VENDOR_CLASS_ID "VendorClassID"
#define CONF_ENABLE_ONLINE_CHECK "EnableOnlineCheck"
+#define CONF_ENABLE_ONLINE_TO_READY_TRANSITION "EnableOnlineToReadyTransition"
+#define CONF_ONLINE_CHECK_INITIAL_INTERVAL "OnlineCheckInitialInterval"
+#define CONF_ONLINE_CHECK_MAX_INTERVAL "OnlineCheckMaxInterval"
#define CONF_AUTO_CONNECT_ROAMING_SERVICES "AutoConnectRoamingServices"
#define CONF_ACD "AddressConflictDetection"
#define CONF_USE_GATEWAYS_AS_TIMESERVERS "UseGatewaysAsTimeservers"
CONF_BG_SCAN,
CONF_PREF_TIMESERVERS,
CONF_AUTO_CONNECT_TECHS,
+ CONF_FAVORITE_TECHS,
CONF_ALWAYS_CONNECTED_TECHS,
CONF_PREFERRED_TECHS,
CONF_FALLBACK_NAMESERVERS,
CONF_ENABLE_6TO4,
CONF_VENDOR_CLASS_ID,
CONF_ENABLE_ONLINE_CHECK,
+ CONF_ENABLE_ONLINE_TO_READY_TRANSITION,
+ CONF_ONLINE_CHECK_INITIAL_INTERVAL,
+ CONF_ONLINE_CHECK_MAX_INTERVAL,
CONF_AUTO_CONNECT_ROAMING_SERVICES,
CONF_ACD,
CONF_USE_GATEWAYS_AS_TIMESERVERS,
char **interfaces;
char **str_list;
char **tethering;
- char *vendor_class_id;
+ char *string;
gsize len;
- int timeout;
+ int integer;
if (!config) {
connman_settings.auto_connect =
g_clear_error(&error);
- timeout = g_key_file_get_integer(config, "General",
+ integer = g_key_file_get_integer(config, "General",
CONF_TIMEOUT_INPUTREQ, &error);
- if (!error && timeout >= 0)
- connman_settings.timeout_inputreq = timeout * 1000;
+ if (!error && integer >= 0)
+ connman_settings.timeout_inputreq = integer * 1000;
g_clear_error(&error);
- timeout = g_key_file_get_integer(config, "General",
+ integer = g_key_file_get_integer(config, "General",
CONF_TIMEOUT_BROWSERLAUNCH, &error);
- if (!error && timeout >= 0)
- connman_settings.timeout_browserlaunch = timeout * 1000;
+ if (!error && integer >= 0)
+ connman_settings.timeout_browserlaunch = integer * 1000;
g_clear_error(&error);
g_clear_error(&error);
- vendor_class_id = __connman_config_get_string(config, "General",
+ string = __connman_config_get_string(config, "General",
CONF_VENDOR_CLASS_ID, &error);
if (!error)
- connman_settings.vendor_class_id = vendor_class_id;
+ connman_settings.vendor_class_id = string;
g_clear_error(&error);
g_clear_error(&error);
boolean = __connman_config_get_bool(config, "General",
+ CONF_ENABLE_ONLINE_TO_READY_TRANSITION, &error);
+ if (!error) {
+ connman_settings.enable_online_to_ready_transition = boolean;
+ }
+
+ g_clear_error(&error);
+
+ integer = g_key_file_get_integer(config, "General",
+ CONF_ONLINE_CHECK_INITIAL_INTERVAL, &error);
+ if (!error && integer >= 0)
+ connman_settings.online_check_initial_interval = integer;
+
+ g_clear_error(&error);
+
+ integer = g_key_file_get_integer(config, "General",
+ CONF_ONLINE_CHECK_MAX_INTERVAL, &error);
+ if (!error && integer >= 0)
+ connman_settings.online_check_max_interval = integer;
+
+ g_clear_error(&error);
+
+ if (connman_settings.online_check_initial_interval < 1 ||
+ connman_settings.online_check_initial_interval >
+ connman_settings.online_check_max_interval) {
+ connman_warn("Incorrect online check intervals [%u, %u]",
+ connman_settings.online_check_initial_interval,
+ connman_settings.online_check_max_interval);
+ connman_settings.online_check_initial_interval =
+ DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL;
+ connman_settings.online_check_max_interval =
+ DEFAULT_ONLINE_CHECK_MAX_INTERVAL;
+ }
+
+ boolean = __connman_config_get_bool(config, "General",
CONF_AUTO_CONNECT_ROAMING_SERVICES, &error);
if (!error)
connman_settings.auto_connect_roaming_services = boolean;
{ NULL },
};
-const char *connman_option_get_string(const char *key)
+char *connman_setting_get_string(const char *key)
{
if (g_str_equal(key, CONF_VENDOR_CLASS_ID))
return connman_settings.vendor_class_id;
if (g_str_equal(key, CONF_ENABLE_ONLINE_CHECK))
return connman_settings.enable_online_check;
+ if (g_str_equal(key, CONF_ENABLE_ONLINE_TO_READY_TRANSITION))
+ return connman_settings.enable_online_to_ready_transition;
+
if (g_str_equal(key, CONF_AUTO_CONNECT_ROAMING_SERVICES))
return connman_settings.auto_connect_roaming_services;
return false;
}
+unsigned int connman_setting_get_uint(const char *key)
+{
+ if (g_str_equal(key, CONF_ONLINE_CHECK_INITIAL_INTERVAL))
+ return connman_settings.online_check_initial_interval;
+
+ if (g_str_equal(key, CONF_ONLINE_CHECK_MAX_INTERVAL))
+ return connman_settings.online_check_max_interval;
+
+ return 0;
+}
+
char **connman_setting_get_string_list(const char *key)
{
if (g_str_equal(key, CONF_PREF_TIMESERVERS))
g_strfreev(connman_settings.fallback_nameservers);
g_strfreev(connman_settings.blacklisted_interfaces);
g_strfreev(connman_settings.tethering_technologies);
+ g_free(connman_settings.vendor_class_id);
g_free(option_debug);
g_free(option_wifi);
# Default value is true.
# EnableOnlineCheck = false
+# Range of intervals between two online check requests.
+# When an online check request fails, another one is triggered after a
+# longer interval. The intervals follow the power of two series of numbers
+# between OnlineCheckInitialInterval and OnlineCheckMaxInterval.
+# Default range is [1, 12], corresponding to the following intervals, in
+# seconds: 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121 and 144.
+# OnlineCheckInitialInterval = 1
+# OnlineCheckMaxInterval = 12
+
+# WARNING: Experimental feature!!!
+# In addition to EnableOnlineCheck setting, enable or disable use of HTTP GET
+# to detect the loss of end-to-end connectivity.
+# If this setting is false, when the default service transitions to ONLINE
+# state, the HTTP GET request is no more called until next cycle, initiated
+# by a transition of the default service to DISCONNECT state.
+# If this setting is true, the HTTP GET request keeps beeing called to guarantee
+# that end-to-end connectivity is still successful. If not, the default service
+# will transition to READY state, enabling another service to become the
+# default one, in replacement.
+# EnableOnlineToReadyTransition = false
+
# List of technologies with AutoConnect = true which are always connected
# regardless of PreferredTechnologies setting. Default value is empty and
# will connect a technology only if it is at a higher preference than any
return 0;
}
+int connman_network_set_autoconnect(struct connman_network *network,
+ bool autoconnect)
+{
+ if (!network->driver || !network->driver->set_autoconnect)
+ return 0;
+ return network->driver->set_autoconnect(network, autoconnect);
+}
+
+bool __connman_network_native_autoconnect(struct connman_network *network)
+{
+ if (!network->driver || !network->driver->set_autoconnect)
+ return false;
+ return true;
+}
+
uint16_t connman_network_get_wifi_channel(struct connman_network *network)
{
return network->wifi.channel;
err = __connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
- gateway, NULL, prefixlen, broadcast);
+ gateway, NULL, prefixlen, broadcast, true);
if (err < 0)
goto error;
service = g_malloc0(sizeof(struct _peer_service));
service->type = type;
- service->data = g_memdup(data, data_length * sizeof(unsigned char));
+ if (data_length > 0) {
+ service->data = g_malloc(data_length * sizeof(unsigned char));
+ memcpy(service->data, data, data_length * sizeof(unsigned char));
+ }
service->length = data_length;
peer->services = g_slist_prepend(peer->services, service);
DBusMessageIter *iter)
{
const char *host, *domain, *type;
+ dbus_bool_t split_routing;
if (!provider->driver || !provider->driver->get_property)
return;
if (type)
connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
&type);
+
+ if (provider->vpn_service) {
+ split_routing = connman_provider_is_split_routing(provider);
+ connman_dbus_dict_append_basic(iter, "SplitRouting",
+ DBUS_TYPE_BOOLEAN, &split_routing);
+ }
}
struct connman_provider *
return provider->identifier;
}
+const char * __connman_provider_get_transport_ident(
+ struct connman_provider *provider)
+{
+ if (provider && provider && provider->driver && provider->driver->get_property)
+ return provider->driver->get_property(provider, "Transport");
+
+ return NULL;
+}
+
int connman_provider_set_string(struct connman_provider *provider,
const char *key, const char *value)
{
__connman_service_save(provider->vpn_service);
}
+bool connman_provider_is_split_routing(struct connman_provider *provider)
+{
+ if (!provider || !provider->vpn_service)
+ return false;
+
+ return __connman_service_is_split_routing(provider->vpn_service);
+}
+
+int connman_provider_set_split_routing(struct connman_provider *provider,
+ bool split_routing)
+{
+ struct connman_service *service;
+ enum connman_ipconfig_type type;
+ int service_index;
+ int vpn_index;
+ bool service_split_routing;
+ int err = 0;
+
+ DBG("");
+
+ if (!provider || !provider->vpn_service)
+ return -EINVAL;
+
+ service_split_routing = __connman_service_is_split_routing(
+ provider->vpn_service);
+
+ if (service_split_routing == split_routing) {
+ DBG("split_routing already set %s",
+ split_routing ? "true" : "false");
+ return -EALREADY;
+ }
+
+ switch (provider->family) {
+ case AF_INET:
+ type = CONNMAN_IPCONFIG_TYPE_IPV4;
+ break;
+ case AF_INET6:
+ type = CONNMAN_IPCONFIG_TYPE_IPV6;
+ break;
+ case AF_UNSPEC:
+ type = CONNMAN_IPCONFIG_TYPE_ALL;
+ break;
+ default:
+ type = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
+ }
+
+ if (!__connman_service_is_connected_state(provider->vpn_service,
+ type)) {
+ DBG("%p VPN not connected", provider->vpn_service);
+ goto save;
+ }
+
+ vpn_index = __connman_service_get_index(provider->vpn_service);
+ service_index = __connman_connection_get_vpn_phy_index(vpn_index);
+ service = __connman_service_lookup_from_index(service_index);
+ if (!service)
+ goto save;
+
+ if (split_routing)
+ err = __connman_service_move(service, provider->vpn_service,
+ true);
+ else
+ err = __connman_service_move(provider->vpn_service, service,
+ true);
+
+ if (err) {
+ connman_warn("cannot move service %p and VPN %p error %d",
+ service, provider->vpn_service, err);
+
+ /*
+ * In case of error notify vpnd about the current split routing
+ * state.
+ */
+ __connman_service_split_routing_changed(provider->vpn_service);
+ goto out;
+ }
+
+save:
+ __connman_service_set_split_routing(provider->vpn_service,
+ split_routing);
+ __connman_service_save(provider->vpn_service);
+
+out:
+ return err;
+}
+
+int connman_provider_get_family(struct connman_provider *provider)
+{
+ if (!provider)
+ return AF_UNSPEC;
+
+ return provider->family;
+}
+
static void unregister_provider(gpointer data)
{
struct connman_provider *provider = data;
} else if (strcmp(line + 8, "bond") == 0) {
interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET;
interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET;
+ } else if (strcmp(line + 8, "dsa") == 0) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET;
+ interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET;
} else {
interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN;
static unsigned int vpn_autoconnect_id = 0;
static struct connman_service *current_default = NULL;
static bool services_dirty = false;
+static bool enable_online_to_ready_transition = false;
+static unsigned int online_check_initial_interval = 0;
+static unsigned int online_check_max_interval = 0;
struct connman_stats {
bool valid;
bool wps;
bool wps_advertizing;
guint online_timeout;
- int online_check_interval_ipv4;
- int online_check_interval_ipv6;
+ unsigned int online_check_interval_ipv4;
+ unsigned int online_check_interval_ipv6;
bool do_split_routing;
bool new_service;
bool hidden_service;
return "auto";
case CONNMAN_SERVICE_CONNECT_REASON_SESSION:
return "session";
+ case CONNMAN_SERVICE_CONNECT_REASON_NATIVE:
+ return "native";
}
return "unknown";
return CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN;
}
-static void set_split_routing(struct connman_service *service, bool value)
+void __connman_service_split_routing_changed(struct connman_service *service)
+{
+ dbus_bool_t split_routing;
+
+ if (!service->path)
+ return;
+
+ if (!allow_property_changed(service))
+ return;
+
+ split_routing = service->do_split_routing;
+ if (!connman_dbus_property_changed_basic(service->path,
+ CONNMAN_SERVICE_INTERFACE, "SplitRouting",
+ DBUS_TYPE_BOOLEAN, &split_routing))
+ connman_warn("cannot send SplitRouting property change on %s",
+ service->identifier);
+}
+
+void __connman_service_set_split_routing(struct connman_service *service,
+ bool value)
{
if (service->type != CONNMAN_SERVICE_TYPE_VPN)
return;
service->order = 0;
else
service->order = 10;
+
+ /*
+ * In order to make sure the value is propagated also when loading the
+ * VPN service signal the value regardless of the value change.
+ */
+ __connman_service_split_routing_changed(service);
}
int __connman_service_load_modifiable(struct connman_service *service)
case CONNMAN_SERVICE_TYPE_P2P:
break;
case CONNMAN_SERVICE_TYPE_VPN:
- set_split_routing(service, g_key_file_get_boolean(keyfile,
- service->identifier,
- "SplitRouting", NULL));
+ __connman_service_set_split_routing(service,
+ g_key_file_get_boolean(keyfile,
+ service->identifier,
+ "SplitRouting", NULL));
/* fall through */
case CONNMAN_SERVICE_TYPE_WIFI:
case CONNMAN_SERVICE_TYPE_P2P:
break;
case CONNMAN_SERVICE_TYPE_VPN:
- set_split_routing(service, g_key_file_get_boolean(keyfile,
- service->identifier,
- "SplitRouting", NULL));
+ __connman_service_set_split_routing(service,
+ g_key_file_get_boolean(keyfile,
+ service->identifier,
+ "SplitRouting", NULL));
autoconnect = g_key_file_get_boolean(keyfile,
service->identifier, "AutoConnect", &error);
str = g_key_file_get_string(keyfile,
service->identifier, "Passphrase", NULL);
if (str) {
+ char *dec = g_strcompress(str);
+ g_free(str);
g_free(service->passphrase);
- service->passphrase = str;
+ service->passphrase = dec;
}
if (service->ipconfig_ipv4)
g_free(str);
}
- if (service->passphrase && strlen(service->passphrase) > 0)
+ if (service->passphrase && strlen(service->passphrase) > 0) {
+ char *enc = g_strescape(service->passphrase, NULL);
g_key_file_set_string(keyfile, service->identifier,
- "Passphrase", service->passphrase);
+ "Passphrase", enc);
+ g_free(enc);
+ }
if (service->ipconfig_ipv4)
__connman_ipconfig_save(service->ipconfig_ipv4, keyfile,
"Default service remains in READY state.");
return;
}
+ enable_online_to_ready_transition =
+ connman_setting_get_bool("EnableOnlineToReadyTransition");
+ online_check_initial_interval =
+ connman_setting_get_uint("OnlineCheckInitialInterval");
+ online_check_max_interval =
+ connman_setting_get_uint("OnlineCheckMaxInterval");
if (type != CONNMAN_IPCONFIG_TYPE_IPV4 || check_proxy_setup(service)) {
cancel_online_check(service);
connman_setting_get_bool("AllowDomainnameUpdates"))
__connman_utsname_set_domainname(service->domainname);
+ if (__connman_service_is_connected_state(service,
+ CONNMAN_IPCONFIG_TYPE_IPV4))
+ __connman_service_wispr_start(service,
+ CONNMAN_IPCONFIG_TYPE_IPV4);
+
+ if (__connman_service_is_connected_state(service,
+ CONNMAN_IPCONFIG_TYPE_IPV6))
+ __connman_service_wispr_start(service,
+ CONNMAN_IPCONFIG_TYPE_IPV6);
+
/*
* Connect VPN automatically when new default service
* is set and connected, unless new default is VPN
service->autoconnect = autoconnect;
autoconnect_changed(service);
+ connman_network_set_autoconnect(service->network, autoconnect);
+
return true;
}
return;
/*
+ * Only user interaction should get VPN or WIFI connected in failure
+ * state.
+ */
+ if (service->state == CONNMAN_SERVICE_STATE_FAILURE &&
+ reason != CONNMAN_SERVICE_CONNECT_REASON_USER &&
+ (service->type == CONNMAN_SERVICE_TYPE_VPN ||
+ service->type == CONNMAN_SERVICE_TYPE_WIFI))
+ return;
+
+ /*
+ * Do not use the builtin auto connect, instead rely on the
+ * native auto connect feature of the service.
+ */
+ if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_NATIVE)
+ return;
+
+ /*
* Run service auto connect for other than VPN services. Afterwards
* start also VPN auto connect process.
*/
if (service->type != CONNMAN_SERVICE_TYPE_VPN)
__connman_service_auto_connect(reason);
- /* Only user interaction should get VPN connected in failure state. */
- else if (service->state == CONNMAN_SERVICE_STATE_FAILURE &&
- reason != CONNMAN_SERVICE_CONNECT_REASON_USER)
- return;
vpn_auto_connect();
}
return err;
}
-/*
- * We set the timeout to 1 sec so that we have a chance to get
- * necessary IPv6 router advertisement messages that might have
- * DNS data etc.
- */
-#define ONLINE_CHECK_INITIAL_INTERVAL 1
-#define ONLINE_CHECK_MAX_INTERVAL 12
-
void __connman_service_wispr_start(struct connman_service *service,
enum connman_ipconfig_type type)
{
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
service->online_check_interval_ipv4 =
- ONLINE_CHECK_INITIAL_INTERVAL;
+ online_check_initial_interval;
else
service->online_check_interval_ipv6 =
- ONLINE_CHECK_INITIAL_INTERVAL;
+ online_check_initial_interval;
__connman_wispr_start(service, type);
}
service_save(service);
timeservers_configuration_changed(service);
-
- if (service == connman_service_get_default())
- __connman_timeserver_sync(service);
+ __connman_timeserver_conf_update(service);
} else if (g_str_equal(name, "Domains.Configuration")) {
DBusMessageIter entry;
continue;
}
+ if (service->connect_reason ==
+ CONNMAN_SERVICE_CONNECT_REASON_NATIVE) {
+ DBG("service %p uses native autonnect, skip", service);
+ continue;
+ }
+
if (service->pending ||
is_connecting(service->state) ||
is_connected(service->state)) {
struct connman_service *temp = list->data;
if (!is_connecting(temp->state) && !is_connected(temp->state))
- break;
+ continue;
if (service == temp)
continue;
services_notify->id = g_timeout_add(100, service_send_changed, NULL);
}
-static DBusMessage *move_service(DBusConnection *conn,
- DBusMessage *msg, void *user_data,
- bool before)
+int __connman_service_move(struct connman_service *service,
+ struct connman_service *target, bool before)
{
- struct connman_service *service = user_data;
- struct connman_service *target;
- const char *path;
enum connman_ipconfig_method target4, target6;
enum connman_ipconfig_method service4, service6;
DBG("service %p", service);
- dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID);
+ if (!service)
+ return -EINVAL;
if (!service->favorite)
- return __connman_error_not_supported(msg);
+ return -EOPNOTSUPP;
- target = find_service(path);
if (!target || !target->favorite || target == service)
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
if (target->type == CONNMAN_SERVICE_TYPE_VPN) {
/*
connman_info("Cannot move service. "
"No routes defined for provider %s",
__connman_provider_get_ident(target->provider));
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
- set_split_routing(target, true);
+ __connman_service_set_split_routing(target, true);
} else
- set_split_routing(target, false);
+ __connman_service_set_split_routing(target, false);
- set_split_routing(service, false);
+ __connman_service_set_split_routing(service, false);
target4 = __connman_ipconfig_get_method(target->ipconfig_ipv4);
target6 = __connman_ipconfig_get_method(target->ipconfig_ipv6);
if (service6 != CONNMAN_IPCONFIG_METHOD_OFF) {
if (!check_suitable_state(target->state_ipv6,
service->state_ipv6))
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
}
if (service4 != CONNMAN_IPCONFIG_METHOD_OFF) {
if (!check_suitable_state(target->state_ipv4,
service->state_ipv4))
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
}
if (target6 != CONNMAN_IPCONFIG_METHOD_OFF) {
if (!check_suitable_state(target->state_ipv6,
service->state_ipv6))
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
}
if (target4 != CONNMAN_IPCONFIG_METHOD_OFF) {
if (!check_suitable_state(target->state_ipv4,
service->state_ipv4))
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
}
service_schedule_changed();
+ return 0;
+}
+
+static DBusMessage *move_service(DBusConnection *conn,
+ DBusMessage *msg, void *user_data,
+ bool before)
+{
+ struct connman_service *service = user_data;
+ struct connman_service *target;
+ const char *path;
+ int err;
+
+ DBG("service %p", service);
+
+ dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+
+ target = find_service(path);
+
+ err = __connman_service_move(service, target, before);
+ switch (err) {
+ case 0:
+ break;
+ case -EINVAL:
+ return __connman_error_invalid_service(msg);
+ case -EOPNOTSUPP:
+ return __connman_error_not_supported(msg);
+ default:
+ connman_warn("unsupported error code %d in move_service()",
+ err);
+ break;
+ }
+
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
g_hash_table_remove(service_hash, service->identifier);
}
+static gint service_compare(gconstpointer a, gconstpointer b);
+
+static gint service_compare_vpn(struct connman_service *a,
+ struct connman_service *b)
+{
+ struct connman_provider *provider;
+ struct connman_service *service;
+ struct connman_service *transport;
+ const char *ident;
+ bool reverse;
+
+ if (a->provider) {
+ provider = a->provider;
+ service = b;
+ reverse = false;
+ } else if (b->provider) {
+ provider = b->provider;
+ service = a;
+ reverse = true;
+ } else {
+ return 0;
+ }
+
+ ident = __connman_provider_get_transport_ident(provider);
+ transport = connman_service_lookup_from_identifier(ident);
+ if (!transport)
+ return 0;
+
+ if (reverse)
+ return service_compare(service, transport);
+
+ return service_compare(transport, service);
+}
+
static gint service_compare(gconstpointer a, gconstpointer b)
{
struct connman_service *service_a = (void *) a;
b_connected = is_connected(state_b);
if (a_connected && b_connected) {
+ int rval;
+
+ /* Compare the VPN transport and the service */
+ if ((service_a->type == CONNMAN_SERVICE_TYPE_VPN ||
+ service_b->type == CONNMAN_SERVICE_TYPE_VPN) &&
+ service_b->type != service_a->type) {
+ rval = service_compare_vpn(service_a, service_b);
+ if (rval)
+ return rval;
+ }
+
if (service_a->order > service_b->order)
return -1;
__connman_service_clear_error(service);
service_complete(service);
+ service_list_sort();
__connman_connection_update_gateway();
}
}
goto done;
}
- if (service->hidden && name_len > 0 && name_len <= 32) {
- device = connman_network_get_device(service->network);
- security = connman_network_get_string(service->network,
- "WiFi.Security");
- err = __connman_device_request_hidden_scan(device,
- name, name_len,
- identity, passphrase,
- security, user_data);
+ if (service->hidden) {
+ if (name_len > 0 && name_len <= 32) {
+ device = connman_network_get_device(service->network);
+ security = connman_network_get_string(service->network,
+ "WiFi.Security");
+ err = __connman_device_request_hidden_scan(device,
+ name, name_len,
+ identity, passphrase,
+ security, user_data);
+ } else {
+ err = -EINVAL;
+ }
if (err < 0)
__connman_service_return_error(service, -err,
user_data);
report_error_cb,
get_dbus_sender(service),
NULL);
+ goto notifier;
}
service_complete(service);
break;
__connman_connection_update_gateway();
+notifier:
if ((old_state == CONNMAN_SERVICE_STATE_ONLINE &&
new_state != CONNMAN_SERVICE_STATE_READY) ||
(old_state == CONNMAN_SERVICE_STATE_READY &&
return FALSE;
}
-int __connman_service_online_check_failed(struct connman_service *service,
- enum connman_ipconfig_type type)
+void __connman_service_online_check(struct connman_service *service,
+ enum connman_ipconfig_type type,
+ bool success)
{
GSourceFunc redo_func;
- int *interval;
+ unsigned int *interval;
+ enum connman_service_state current_state;
if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
interval = &service->online_check_interval_ipv4;
redo_func = redo_wispr_ipv6;
}
+ if(!enable_online_to_ready_transition)
+ goto redo_func;
+
+ if (success) {
+ *interval = online_check_max_interval;
+ } else {
+ current_state = service->state;
+ downgrade_state(service);
+ if (current_state != service->state)
+ *interval = online_check_initial_interval;
+ if (service != connman_service_get_default()) {
+ return;
+ }
+ }
+
+redo_func:
DBG("service %p type %s interval %d", service,
__connman_ipconfig_type2string(type), *interval);
redo_func, connman_service_ref(service));
/* Increment the interval for the next time, set a maximum timeout of
- * ONLINE_CHECK_MAX_INTERVAL * ONLINE_CHECK_MAX_INTERVAL seconds.
+ * online_check_max_interval seconds * online_check_max_interval seconds.
*/
- if (*interval < ONLINE_CHECK_MAX_INTERVAL)
+ if (*interval < online_check_max_interval)
(*interval)++;
-
- return EAGAIN;
}
int __connman_service_ipconfig_indicate_state(struct connman_service *service,
__connman_service_clear_error(service);
+ if (service->network && service->autoconnect &&
+ __connman_network_native_autoconnect(service->network)) {
+ DBG("service %p switch connecting reason to native", service);
+ reason = CONNMAN_SERVICE_CONNECT_REASON_NATIVE;
+ }
+
err = service_connect(service);
DBG("service %p err %d", service, err);
return err;
}
-int __connman_service_disconnect_all(void)
-{
- struct connman_service *service;
- GSList *services = NULL, *list;
- GList *iter;
-
- DBG("");
-
- for (iter = service_list; iter; iter = iter->next) {
- service = iter->data;
-
- if (!is_connected(service->state))
- break;
-
- services = g_slist_prepend(services, service);
- }
-
- for (list = services; list; list = list->next) {
- struct connman_service *service = list->data;
-
- service->ignore = true;
-
- __connman_service_disconnect(service);
- }
-
- g_slist_free(services);
-
- return 0;
-}
-
/**
* lookup_by_identifier:
* @identifier: service identifier
DBG("path %s", service->path);
- if (__connman_config_provision_service(service) < 0)
- service_load(service);
-
g_dbus_register_interface(connection, service->path,
CONNMAN_SERVICE_INTERFACE,
service_methods, service_signals,
NULL, service, NULL);
+ if (__connman_config_provision_service(service) < 0)
+ service_load(service);
+
service_list_sort();
__connman_connection_update_gateway();
service_list_sort();
}
+static void trigger_autoconnect(struct connman_service *service)
+{
+ struct connman_device *device;
+ bool native;
+
+ if (!service->favorite)
+ return;
+
+ native = __connman_network_native_autoconnect(service->network);
+ if (native && service->autoconnect) {
+ DBG("trigger native autoconnect");
+ connman_network_set_autoconnect(service->network, true);
+ return;
+ }
+
+ device = connman_network_get_device(service->network);
+ if (device && connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_UNKNOWN))
+ return;
+
+ switch (service->type) {
+ case CONNMAN_SERVICE_TYPE_UNKNOWN:
+ case CONNMAN_SERVICE_TYPE_SYSTEM:
+ case CONNMAN_SERVICE_TYPE_P2P:
+ break;
+
+ case CONNMAN_SERVICE_TYPE_GADGET:
+ case CONNMAN_SERVICE_TYPE_ETHERNET:
+ if (service->autoconnect) {
+ __connman_service_connect(service,
+ CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+ break;
+ }
+
+ /* fall through */
+ case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+ case CONNMAN_SERVICE_TYPE_GPS:
+ case CONNMAN_SERVICE_TYPE_VPN:
+ case CONNMAN_SERVICE_TYPE_WIFI:
+ case CONNMAN_SERVICE_TYPE_CELLULAR:
+ do_auto_connect(service, CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+ break;
+ }
+}
+
/**
* __connman_service_create_from_network:
* @network: network structure
struct connman_service * __connman_service_create_from_network(struct connman_network *network)
{
struct connman_service *service;
- struct connman_device *device;
const char *ident, *group;
char *name;
unsigned int *auto_connect_types, *favorite_types;
service_register(service);
service_schedule_added(service);
- if (service->favorite) {
- device = connman_network_get_device(service->network);
- if (device && !connman_device_get_scanning(device,
- CONNMAN_SERVICE_TYPE_UNKNOWN)) {
-
- switch (service->type) {
- case CONNMAN_SERVICE_TYPE_UNKNOWN:
- case CONNMAN_SERVICE_TYPE_SYSTEM:
- case CONNMAN_SERVICE_TYPE_P2P:
- break;
-
- case CONNMAN_SERVICE_TYPE_GADGET:
- case CONNMAN_SERVICE_TYPE_ETHERNET:
- if (service->autoconnect) {
- __connman_service_connect(service,
- CONNMAN_SERVICE_CONNECT_REASON_AUTO);
- break;
- }
-
- /* fall through */
- case CONNMAN_SERVICE_TYPE_BLUETOOTH:
- case CONNMAN_SERVICE_TYPE_GPS:
- case CONNMAN_SERVICE_TYPE_VPN:
- case CONNMAN_SERVICE_TYPE_WIFI:
- case CONNMAN_SERVICE_TYPE_CELLULAR:
- do_auto_connect(service,
- CONNMAN_SERVICE_CONNECT_REASON_AUTO);
- break;
- }
- }
- }
+ trigger_autoconnect(service);
__connman_notifier_service_add(service, service->name);
struct connman_service *service;
struct connman_service_info *info;
GSList *service_list = NULL;
- enum connman_service_state state = CONNMAN_SESSION_STATE_DISCONNECTED;
+ enum connman_service_state state = CONNMAN_SERVICE_STATE_DISCONNECT;
g_hash_table_iter_init(&iter, service_hash);
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
+#include <string.h>
#include "src/shared/util.h"
"Tethering.Identifier",
technology->tethering_ident);
- if (technology->tethering_passphrase)
+ if (technology->tethering_passphrase) {
+ char *enc = g_strescape(technology->tethering_passphrase, NULL);
g_key_file_set_string(keyfile, identifier,
- "Tethering.Passphrase",
- technology->tethering_passphrase);
+ "Tethering.Passphrase", enc);
+ g_free(enc);
+ }
done:
g_free(identifier);
gchar *identifier;
GError *error = NULL;
bool enable, need_saving = false;
+ char *enc;
DBG("technology %p", technology);
technology->tethering_ident = g_key_file_get_string(keyfile,
identifier, "Tethering.Identifier", NULL);
- technology->tethering_passphrase = g_key_file_get_string(keyfile,
+ enc = g_key_file_get_string(keyfile,
identifier, "Tethering.Passphrase", NULL);
+ if (enc)
+ technology->tethering_passphrase = g_strcompress(enc);
done:
g_free(identifier);
if ((__connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, pn->index, AF_INET,
- server_ip, peer_ip, prefixlen, NULL)) < 0) {
+ server_ip, peer_ip, prefixlen, NULL, true))
+ < 0) {
DBG("address setting failed");
return;
}
#include <stdlib.h>
#include <gweb/gresolv.h>
#include <netdb.h>
+#include <sys/time.h>
#include "connman.h"
static char *ts_current = NULL;
static int ts_recheck_id = 0;
static int ts_backoff_id = 0;
+static bool ts_is_synced = false;
static GResolv *resolv = NULL;
static int resolv_id = 0;
static void ntp_callback(bool success, void *user_data)
{
+ dbus_uint64_t timestamp;
+ struct timeval tv;
+
DBG("success %d", success);
- if (!success)
+ __connman_timeserver_set_synced(success);
+ if (!success) {
sync_next();
+ return;
+ }
+
+ if (gettimeofday(&tv, NULL) < 0) {
+ connman_warn("Failed to get current time");
+ }
+
+ timestamp = tv.tv_sec;
+ connman_dbus_property_changed_basic(
+ CONNMAN_MANAGER_PATH,
+ CONNMAN_CLOCK_INTERFACE, "Time",
+ DBUS_TYPE_UINT64, ×tamp);
}
static void save_timeservers(char **servers)
static gboolean ts_recheck(gpointer user_data)
{
+ struct connman_service *service;
GSList *ts;
ts = __connman_timeserver_get_all(connman_service_get_default());
g_slist_free_full(ts, g_free);
- __connman_timeserver_sync(NULL);
+ service = connman_service_get_default();
+ __connman_timeserver_sync(service);
return FALSE;
}
NULL);
}
-/*
- * This function must be called every time the default service changes, the
- * service timeserver(s) or gateway changes or the global timeserver(s) changes.
- */
-int __connman_timeserver_sync(struct connman_service *default_service)
+static void ts_reset(struct connman_service *service)
{
- struct connman_service *service;
char **nameservers;
int i;
- if (default_service)
- service = default_service;
- else
- service = connman_service_get_default();
-
- if (!service)
- return -EINVAL;
+ if (!resolv)
+ return;
- if (service == ts_service)
- return -EALREADY;
+ __connman_timeserver_set_synced(false);
- if (!resolv)
- return 0;
/*
* Before we start creating the new timeserver list we must stop
* any ongoing ntp query and server resolution.
g_resolv_flush_nameservers(resolv);
nameservers = connman_service_get_nameservers(service);
- if (!nameservers)
- return -EINVAL;
-
- for (i = 0; nameservers[i]; i++)
- g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
+ if (nameservers) {
+ for (i = 0; nameservers[i]; i++)
+ g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
- g_strfreev(nameservers);
+ g_strfreev(nameservers);
+ }
g_slist_free_full(timeservers_list, g_free);
if (!timeservers_list) {
DBG("No timeservers set.");
- return 0;
+ return;
}
ts_recheck_enable();
ts_service = service;
timeserver_sync_start();
+}
- return 0;
+void __connman_timeserver_sync(struct connman_service *service)
+{
+ if (!service || ts_service == service)
+ return;
+
+ ts_reset(service);
+}
+
+void __connman_timeserver_conf_update(struct connman_service *service)
+{
+ if (!service || (ts_service && ts_service != service))
+ return;
+
+ ts_reset(service);
+}
+
+
+bool __connman_timeserver_is_synced(void)
+{
+ return ts_is_synced;
+}
+
+void __connman_timeserver_set_synced(bool status)
+{
+ dbus_bool_t is_synced;
+
+ if (ts_is_synced == status)
+ return;
+
+ ts_is_synced = status;
+ is_synced = status;
+ connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
+ CONNMAN_CLOCK_INTERFACE, "TimeserverSynced",
+ DBUS_TYPE_BOOLEAN, &is_synced);
}
static int timeserver_start(struct connman_service *service)
g_strfreev(nameservers);
}
- return __connman_timeserver_sync(service);
+ __connman_timeserver_sync(service);
+
+ return 0;
}
static void timeserver_stop(void)
int __connman_timeserver_system_set(char **servers)
{
+ struct connman_service *service;
+
save_timeservers(servers);
- __connman_timeserver_sync(NULL);
+ service = connman_service_get_default();
+ if (service)
+ ts_reset(service);
return 0;
}
static GHashTable *wispr_portal_list = NULL;
+static bool enable_online_to_ready_transition = false;
+
static void connman_wispr_message_init(struct connman_wispr_message *msg)
{
DBG("");
&str))
connman_info("Client-Timezone: %s", str);
- free_connman_wispr_portal_context(wp_context);
+ if (!enable_online_to_ready_transition)
+ free_connman_wispr_portal_context(wp_context);
__connman_service_ipconfig_indicate_state(service,
CONNMAN_SERVICE_STATE_ONLINE, type);
+
+ if (enable_online_to_ready_transition)
+ __connman_service_online_check(service, type, true);
}
static bool wispr_route_request(const char *address, int ai_family,
wp_context->redirect_url, wp_context);
break;
+ case 300:
+ case 301:
case 302:
+ case 303:
+ case 307:
+ case 308:
if (!g_web_supports_tls() ||
!g_web_result_get_header(result, "Location",
&redirect)) {
goto done;
case 400:
case 404:
- if (__connman_service_online_check_failed(wp_context->service,
- wp_context->type) == 0) {
- wispr_portal_error(wp_context);
- free_connman_wispr_portal_context(wp_context);
- return false;
- }
+ __connman_service_online_check(wp_context->service,
+ wp_context->type, false);
break;
case 505:
void __connman_wispr_stop(struct connman_service *service)
{
+ struct connman_wispr_portal *wispr_portal;
int index;
DBG("service %p", service);
if (index < 0)
return;
- g_hash_table_remove(wispr_portal_list, GINT_TO_POINTER(index));
+ wispr_portal = g_hash_table_lookup(wispr_portal_list,
+ GINT_TO_POINTER(index));
+ if (!wispr_portal)
+ return;
+
+ if (wispr_portal->ipv4_context) {
+ if (service == wispr_portal->ipv4_context->service)
+ g_hash_table_remove(wispr_portal_list,
+ GINT_TO_POINTER(index));
+ }
+
+ if (wispr_portal->ipv6_context) {
+ if (service == wispr_portal->ipv6_context->service)
+ g_hash_table_remove(wispr_portal_list,
+ GINT_TO_POINTER(index));
+ }
}
int __connman_wispr_init(void)
g_direct_equal, NULL,
free_connman_wispr_portal);
+ enable_online_to_ready_transition =
+ connman_setting_get_bool("EnableOnlineToReadyTransition");
+
return 0;
}
#!/usr/bin/python
-import gobject
+from gi.repository import GLib
import dbus
import dbus.mainloop.glib
bus.add_match_string("member=Update,interface=net.connman.Notification")
bus.add_message_filter(message_filter)
- mainloop = gobject.MainLoop()
+ mainloop = GLib.MainLoop()
mainloop.run()
#!/usr/bin/python
-import gobject
+from gi.repository import GLib
import dbus
import dbus.mainloop.glib
signal_name="PropertyChanged",
path_keyword="path")
- mainloop = gobject.MainLoop()
+ mainloop = GLib.MainLoop()
mainloop.run()
from os import O_NONBLOCK
from sys import stdin, stdout, exit, version_info, argv
from fcntl import fcntl, F_GETFL, F_SETFL
-import glib
+from gi.repository import GLib
import dbus
import dbus.mainloop.glib
-import gobject
import argparse
WPA_NAME='fi.w1.wpa_supplicant1'
flags = fcntl(stdin.fileno(), F_GETFL)
flags |= O_NONBLOCK
fcntl(stdin.fileno(), F_SETFL, flags)
- glib.io_add_watch(stdin, glib.IO_IN, self.input_cb)
+ GLib.io_add_watch(stdin, GLib.IO_IN, self.input_cb)
self.prompt()
stdout.flush()
def input_cb(self, fd, event):
- if event != glib.IO_IN:
+ if event != GLib.IO_IN:
return
self.line += fd.read();
return command
def main():
- if version_info.major != 2:
- print('You need to run this under Python 2.x')
- exit(1)
-
parser = argparse.ArgumentParser(description='Connman P2P Test')
command_list = build_args(parser)
bus = dbus.SystemBus()
- mainloop = gobject.MainLoop()
+ mainloop = GLib.MainLoop()
wpa_s = Wpa_s(bus, args.ifname, args.command + params)
#!/usr/bin/python
-import gobject
+from gi.repository import GLib
import dbus
import dbus.service
except:
"Cannot register vpn agent"
- mainloop = gobject.MainLoop()
+ mainloop = GLib.MainLoop()
mainloop.run()
#manager.UnregisterAgent(path)
#!/usr/bin/python
+from gi.repository import GLib
+
import sys
-import gobject
import dbus
import dbus.service
manager.RegisterCounter(path, dbus.UInt32(10), dbus.UInt32(period))
- mainloop = gobject.MainLoop()
+ mainloop = GLib.MainLoop()
mainloop.run()
#manager.UnregisterCounter(path)
#!/usr/bin/python
+from gi.repository import GLib
+
import sys
-import gobject
-import string
import dbus
import dbus.service
import dbus.mainloop.glib
-import glib
-
import traceback
def extract_list(list):
app_path = sys.argv[2]
bus = dbus.SessionBus()
- app_name = "com.example.SessionApplication.%s" % (string.strip(app_path, "/"))
+ app_name = "com.example.SessionApplication.%s" % (str.strip(app_path, "/"))
if sys.argv[1] == "run":
name = dbus.service.BusName(app_name, bus)
- mainloop = gobject.MainLoop()
+ mainloop = GLib.MainLoop()
app = SessionApplication(bus, app_path, mainloop)
BusName=net.connman.vpn
ExecStart=@sbindir@/connman-vpnd -n
StandardOutput=null
-CapabilityBoundingSet=CAP_KILL CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID
+CapabilityBoundingSet=CAP_KILL CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_CHOWN CAP_FOWNER
ProtectHome=read-only
ProtectSystem=full
static gchar *option_noplugin = NULL;
static bool option_detach = true;
static bool option_version = false;
-static bool option_routes = false;
static bool parse_debug(const char *key, const char *value,
gpointer user_data, GError **error)
{ "nodaemon", 'n', G_OPTION_FLAG_REVERSE,
G_OPTION_ARG_NONE, &option_detach,
"Don't fork daemon to background" },
- { "routes", 'r', 0, G_OPTION_ARG_NONE, &option_routes,
- "Create/delete VPN routes" },
{ "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
"Show version information and exit" },
{ NULL },
__connman_inotify_init();
__connman_agent_init();
- __vpn_provider_init(option_routes);
+ __vpn_provider_init();
__vpn_manager_init();
__vpn_ipconfig_init();
__vpn_rtnl_init();
connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
gateway);
+ connman_ipaddress_set_p2p(ipaddress, true);
+
vpn_provider_set_ipaddress(provider, ipaddress);
vpn_provider_set_nameservers(provider, nameservers);
#include <connman/setting.h>
#include <connman/vpn-dbus.h>
+#include <openconnect.h>
+
#include "../vpn-provider.h"
#include "../vpn-agent.h"
static const char *connect_types[] = {"cookie", "cookie_with_userpass",
"userpass", "publickey", "pkcs", NULL};
-static const char *protocols[] = { "anyconnect", "nc", "gp", NULL};
struct oc_private_data {
struct vpn_provider *provider;
char *dbus_sender;
vpn_provider_connect_cb_t cb;
void *user_data;
+
+ GThread *cookie_thread;
+ struct openconnect_info *vpninfo;
+ int fd_cmd;
+ int err;
+
int fd_in;
- int out_ch_id;
int err_ch_id;
- GIOChannel *out_ch;
GIOChannel *err_ch;
enum oc_connect_type connect_type;
- bool interactive;
+ bool tried_passphrase;
};
+typedef void (*request_input_reply_cb_t) (DBusMessage *reply,
+ void *user_data);
+
+static int run_connect(struct oc_private_data *data, const char *cookie);
+static int request_input_credentials_full(
+ struct oc_private_data *data,
+ request_input_reply_cb_t cb,
+ void *user_data);
+
static bool is_valid_protocol(const char* protocol)
{
+ int num_protocols;
+ int i;
+ struct oc_vpn_proto *protos;
+
if (!protocol || !*protocol)
return false;
- return g_strv_contains(protocols, protocol);
+ num_protocols = openconnect_get_supported_protocols(&protos);
+
+ for (i = 0; i < num_protocols; i++)
+ if (!strcmp(protos[i].name, protocol))
+ break;
+
+ openconnect_free_supported_protocols(protos);
+
+ return i < num_protocols;
}
static void oc_connect_done(struct oc_private_data *data, int err)
if (!data || !channel)
return;
- if (data->out_ch == channel) {
- id = data->out_ch_id;
- data->out_ch = NULL;
- data->out_ch_id = 0;
- } else if (data->err_ch == channel) {
+ if (data->err_ch == channel) {
id = data->err_ch_id;
data->err_ch = NULL;
data->err_ch_id = 0;
connman_info("provider %p", data->provider);
+ if (data->vpninfo)
+ openconnect_vpninfo_free(data->vpninfo);
+
if (vpn_provider_get_plugin_data(data->provider) == data)
vpn_provider_set_plugin_data(data->provider, NULL);
if (data->fd_in > 0)
close(data->fd_in);
data->fd_in = -1;
- close_io_channel(data, data->out_ch);
close_io_channel(data, data->err_ch);
g_free(data->dbus_sender);
else
connman_ipaddress_set_ipv6(ipaddress, addressv6,
prefix_len, gateway);
+
+ connman_ipaddress_set_p2p(ipaddress, true);
vpn_provider_set_ipaddress(provider, ipaddress);
vpn_provider_set_domain(provider, domain);
return VPN_STATE_CONNECT;
}
-static ssize_t full_write(int fd, const void *buf, size_t len)
+static ssize_t full_write(int fd, const char *buf, size_t len)
{
ssize_t byte_write;
free_private_data(data);
}
-static gboolean io_channel_out_cb(GIOChannel *source, GIOCondition condition,
- gpointer user_data)
-{
- struct oc_private_data *data;
- char *str;
-
- data = user_data;
-
- if (data->out_ch != source)
- return G_SOURCE_REMOVE;
-
- if ((condition & G_IO_IN) &&
- g_io_channel_read_line(source, &str, NULL, NULL, NULL) ==
- G_IO_STATUS_NORMAL) {
-
- g_strchomp(str);
-
- /* Only cookie is printed to stdout */
- vpn_provider_set_string_hide_value(data->provider,
- "OpenConnect.Cookie", str);
-
- g_free(str);
- } else if (condition & (G_IO_ERR | G_IO_HUP)) {
- connman_info("Out channel termination");
- close_io_channel(data, source);
- return G_SOURCE_REMOVE;
- }
-
- return G_SOURCE_CONTINUE;
-}
-
static bool strv_contains_prefix(const char *strv[], const char *str)
{
int i;
return false;
}
-static void clear_provider_credentials(struct vpn_provider *provider)
+static void clear_provider_credentials(struct vpn_provider *provider,
+ bool clear_pkcs_pass)
{
- const char *keys[] = { "OpenConnect.Username",
+ const char *keys[] = { "OpenConnect.PKCSPassword",
+ "OpenConnect.Username",
"OpenConnect.Password",
- "OpenConnect.PKCSPassword",
"OpenConnect.Cookie",
NULL
};
- int i;
+ size_t i;
connman_info("provider %p", provider);
- for (i = 0; keys[i]; i++) {
+ for (i = !clear_pkcs_pass; keys[i]; i++) {
if (!vpn_provider_get_string_immutable(provider, keys[i]))
vpn_provider_set_string_hide_value(provider, keys[i],
"-");
}
}
-typedef void (* request_input_reply_cb_t) (DBusMessage *reply,
- void *user_data);
+static void __attribute__ ((format(printf, 3, 4))) oc_progress(void *user_data,
+ int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
-static int request_input_credentials(struct oc_private_data *data,
- request_input_reply_cb_t cb);
+ va_start(ap, fmt);
+ msg = g_strdup_vprintf(fmt, ap);
+
+ connman_debug("%s", msg);
+ g_free(msg);
+
+ va_end(ap);
+}
+
+/*
+ * There is no enum / defines for these in openconnect.h, but these values
+ * are based on the comment for openconnect_validate_peer_cert_vfn.
+ */
+enum oc_cert_status {
+ OC_CERT_ACCEPT = 0,
+ OC_CERT_REJECT = 1
+};
+
+struct validate_cert_data {
+ GMutex mutex;
+ GCond cond;
+ const char *reason;
+ struct oc_private_data *data;
+ bool processed;
+ enum oc_cert_status status;
+};
+
+static gboolean validate_cert(void *user_data)
+{
+ struct validate_cert_data *cert_data = user_data;
+ struct oc_private_data *data;
+ const char *server_cert;
+ bool allow_self_signed;
+
+ DBG("");
+
+ g_mutex_lock(&cert_data->mutex);
+
+ data = cert_data->data;
+ server_cert = vpn_provider_get_string(data->provider,
+ "OpenConnect.ServerCert");
+ allow_self_signed = vpn_provider_get_boolean(data->provider,
+ "OpenConnect.AllowSelfSignedCert",
+ false);
+ if (!allow_self_signed) {
+ cert_data->status = OC_CERT_REJECT;
+ } else if (server_cert) {
+ /*
+ * Check peer cert hash may return negative values on errors,
+ * but anything non-zero is acceptable.
+ */
+ cert_data->status = openconnect_check_peer_cert_hash(
+ data->vpninfo,
+ server_cert);
+ } else {
+ /*
+ * We could verify this from the agent at this point, and
+ * release the thread upon reply.
+ */
+ DBG("Server cert hash: %s",
+ openconnect_get_peer_cert_hash(data->vpninfo));
+ vpn_provider_set_string(data->provider,
+ "OpenConnect.ServerCert",
+ openconnect_get_peer_cert_hash(data->vpninfo));
+ cert_data->status = OC_CERT_ACCEPT;
+ }
+
+ cert_data->processed = true;
+ g_cond_signal(&cert_data->cond);
+ g_mutex_unlock(&cert_data->mutex);
+
+ return G_SOURCE_REMOVE;
+}
+
+static int oc_validate_peer_cert(void *user_data, const char *reason)
+{
+ struct validate_cert_data data = { .reason = reason,
+ .data = user_data,
+ .processed = false };
+
+ g_cond_init(&data.cond);
+ g_mutex_init(&data.mutex);
+
+ g_mutex_lock(&data.mutex);
+
+ g_idle_add(validate_cert, &data);
+
+ while (!data.processed)
+ g_cond_wait(&data.cond, &data.mutex);
+
+ g_mutex_unlock(&data.mutex);
+
+ g_mutex_clear(&data.mutex);
+ g_cond_clear(&data.cond);
+
+ return data.status;
+}
+
+struct process_form_data {
+ GMutex mutex;
+ GCond cond;
+ struct oc_auth_form *form;
+ struct oc_private_data *data;
+ bool processed;
+ int status;
+};
static void request_input_pkcs_reply(DBusMessage *reply, void *user_data)
{
- struct oc_private_data *data = user_data;
- const char *password = NULL;
+ struct process_form_data *form_data = user_data;
+ struct oc_private_data *data = form_data->data;
+ struct oc_form_opt *opt;
const char *key;
+ const char *password = NULL;
DBusMessageIter iter, dict;
- int err;
connman_info("provider %p", data->provider);
- if (!reply)
+ if (!reply) {
+ data->err = ENOENT;
goto err;
+ }
- err = vpn_agent_check_and_process_reply_error(reply, data->provider,
- data->task, data->cb, data->user_data);
- if (err) {
- /* Ensure cb is called only once */
+ if ((data->err = vpn_agent_check_and_process_reply_error(reply,
+ data->provider,
+ data->task,
+ data->cb,
+ data->user_data))) {
data->cb = NULL;
data->user_data = NULL;
goto err;
}
- if (!vpn_agent_check_reply_has_dict(reply))
+ if (!vpn_agent_check_reply_has_dict(reply)) {
+ data->err = ENOENT;
goto err;
+ }
dbus_message_iter_init(reply, &iter);
dbus_message_iter_recurse(&iter, &dict);
dbus_message_iter_next(&dict);
}
- if (data->connect_type != OC_CONNECT_PKCS || !password)
+ if (!password)
goto err;
- if (write_data(data->fd_in, password) != 0) {
- connman_error("openconnect failed to take PKCS pass phrase on"
- " stdin");
- goto err;
+ for (opt = form_data->form->opts; opt; opt = opt->next) {
+ if (opt->flags & OC_FORM_OPT_IGNORE)
+ continue;
+
+ if (opt->type == OC_FORM_OPT_PASSWORD &&
+ g_str_has_prefix(opt->name,
+ "openconnect_pkcs")) {
+ opt->_value = strdup(password);
+ form_data->status = OC_FORM_RESULT_OK;
+ data->tried_passphrase = true;
+ break;
+ }
}
- clear_provider_credentials(data->provider);
+ goto out;
- return;
err:
- oc_connect_done(data, EACCES);
+ form_data->status = OC_FORM_RESULT_ERR;
+
+out:
+ form_data->processed = true;
+ g_cond_signal(&form_data->cond);
+ g_mutex_unlock(&form_data->mutex);
}
static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
- gpointer user_data)
+ gpointer user_data)
{
struct oc_private_data *data;
const char *auth_failures[] = {
- /* Login failed */
- "Got HTTP response: HTTP/1.1 401 Unauthorized",
- "Failed to obtain WebVPN cookie",
/* Cookie not valid */
"Got inappropriate HTTP CONNECT response: "
"HTTP/1.1 401 Unauthorized",
/* Invalid cookie */
"VPN service unavailable",
- /* Problem with certificates */
- "SSL connection failure",
- "Creating SSL connection failed",
- "SSL connection cancelled",
NULL
};
const char *conn_failures[] = {
"Failed to open HTTPS connection to",
NULL
};
- /* Handle both PKCS#12 and PKCS#8 failures */
- const char *pkcs_failures[] = {
- "Failed to decrypt PKCS#12 certificate file",
- "Failed to decrypt PKCS#8 certificate file",
- NULL
- };
- /* Handle both PKCS#12 and PKCS#8 requests */
- const char *pkcs_requests[] = {
- "Enter PKCS#12 pass phrase",
- "Enter PKCS#8 pass phrase",
- NULL
- };
- const char *server_key_hash = " --servercert";
+ const char *server_key_hash = " --servercert ";
char *str;
- bool close = false;
int err = 0;
data = user_data;
if ((condition & G_IO_IN)) {
gsize len;
- int pos;
-
- if (!data->interactive) {
- if (g_io_channel_read_line(source, &str, &len, NULL,
- NULL) != G_IO_STATUS_NORMAL)
- err = EIO;
- else
- str[len - 1] = '\0';
- } else {
- GIOStatus status;
- str = g_try_new0(char, OC_MAX_READBUF_LEN);
- if (!str)
- return G_SOURCE_REMOVE;
- for (pos = 0; pos < OC_MAX_READBUF_LEN - 1 ; ++pos) {
- status = g_io_channel_read_chars(source,
- str+pos, 1, &len, NULL);
-
- if (status == G_IO_STATUS_EOF) {
- break;
- } else if (status != G_IO_STATUS_NORMAL) {
- err = EIO;
- break;
- }
-
- /* Ignore control chars and digits at start */
- if (!pos && (g_ascii_iscntrl(str[pos]) ||
- g_ascii_isdigit(str[pos])))
- --pos;
-
- /* Read zero length or no more to read */
- if (!len || g_io_channel_get_buffer_condition(
- source) != G_IO_IN ||
- str[pos] == '\n')
- break;
- }
-
- /*
- * When self signed certificates are allowed and server
- * SHA1 fingerprint is printed to stderr there is a
- * newline char at the end of SHA1 fingerprint.
- */
- if (str[pos] == '\n')
- str[pos] = '\0';
- }
+ if (g_io_channel_read_line(source, &str, &len, NULL,
+ NULL) != G_IO_STATUS_NORMAL)
+ err = EIO;
+ else
+ g_strchomp(str);
connman_info("openconnect: %s", str);
vpn_provider_set_string(data->provider,
"OpenConnect.ServerCert",
- fingerprint);
-
- /*
- * OpenConnect waits for "yes" or "no" as
- * response to certificate acceptance request.
- */
- if (write_data(data->fd_in, "yes") != 0)
- connman_error("openconnect: cannot "
- "write answer to certificate "
- "accept request");
-
+ str + strlen(server_key_hash));
} else {
connman_warn("Self signed certificate is not "
- " allowed");
-
- /*
- * Close IO channel to avoid deadlock as an
- * answer is expected for the certificate
- * accept request.
- */
- close = true;
+ "allowed");
err = ECONNREFUSED;
}
- } else if (strv_contains_prefix(pkcs_failures, str)) {
- connman_warn("PKCS failure: %s", str);
- close = true;
- err = EACCES;
- } else if (strv_contains_prefix(pkcs_requests, str)) {
- connman_info("PKCS file pass phrase request: %s", str);
- err = request_input_credentials(data,
- request_input_pkcs_reply);
-
- if (err != -EINPROGRESS) {
- err = EACCES;
- close = true;
- } else {
- err = 0;
- }
} else if (strv_contains_prefix(auth_failures, str)) {
connman_warn("authentication failed: %s", str);
err = EACCES;
g_free(str);
} else if (condition & (G_IO_ERR | G_IO_HUP)) {
connman_info("Err channel termination");
- close = true;
+ close_io_channel(data, source);
+ return G_SOURCE_REMOVE;
}
if (err) {
switch (err) {
case EACCES:
- clear_provider_credentials(data->provider);
+ clear_provider_credentials(data->provider, true);
break;
case ECONNREFUSED:
/*
oc_connect_done(data, err);
}
- if (close) {
- close_io_channel(data, source);
- return G_SOURCE_REMOVE;
- }
-
return G_SOURCE_CONTINUE;
}
-static int run_connect(struct oc_private_data *data)
+static gboolean process_auth_form(void *user_data)
{
- struct vpn_provider *provider;
- struct connman_task *task;
- const char *vpnhost;
- const char *vpncookie = NULL;
- const char *username;
- const char *password = NULL;
- const char *certificate = NULL;
- const char *private_key;
- const char *setting_str;
- bool setting;
- bool use_stdout = false;
- int fd_out = -1;
- int fd_err;
- int err = 0;
-
- if (!data)
- return -EINVAL;
+ struct process_form_data *form_data = user_data;
+ struct oc_private_data *data = form_data->data;
+ struct oc_form_opt *opt;
+ const char *password;
- provider = data->provider;
- task = data->task;
+ g_mutex_lock(&form_data->mutex);
- connman_info("provider %p task %p", provider, task);
+ DBG("");
switch (data->connect_type) {
- case OC_CONNECT_COOKIE:
- vpncookie = vpn_provider_get_string(provider,
- "OpenConnect.Cookie");
- if (!vpncookie || !g_strcmp0(vpncookie, "-")) {
- err = -EACCES;
- goto done;
- }
-
- connman_task_add_argument(task, "--cookie-on-stdin", NULL);
- break;
+ case OC_CONNECT_USERPASS:
case OC_CONNECT_COOKIE_WITH_USERPASS:
- vpncookie = vpn_provider_get_string(provider,
- "OpenConnect.Cookie");
- /* No cookie set yet, username and password used first */
- if (!vpncookie || !g_strcmp0(vpncookie, "-")) {
- username = vpn_provider_get_string(provider,
- "OpenConnect.Username");
- password = vpn_provider_get_string(provider,
- "OpenConnect.Password");
- if (!username || !password ||
- !g_strcmp0(username, "-") ||
- !g_strcmp0(password, "-")) {
- err = -EACCES;
- goto done;
- }
+ break;
+
+ case OC_CONNECT_PKCS:
+ password = vpn_provider_get_string(data->provider,
+ "OpenConnect.PKCSPassword");
- connman_task_add_argument(task, "--cookieonly", NULL);
- connman_task_add_argument(task, "--user", username);
- connman_task_add_argument(task, "--passwd-on-stdin",
- NULL);
+ for (opt = form_data->form->opts; opt; opt = opt->next) {
+ if (opt->flags & OC_FORM_OPT_IGNORE)
+ continue;
- /* Use stdout only when cookie is to be read. */
- use_stdout = true;
- } else {
- connman_task_add_argument(task, "--cookie-on-stdin",
- NULL);
+ if (opt->type == OC_FORM_OPT_PASSWORD &&
+ g_str_has_prefix(opt->name,
+ "openconnect_pkcs"))
+ break;
}
- break;
- case OC_CONNECT_USERPASS:
- username = vpn_provider_get_string(provider,
- "OpenConnect.Username");
- password = vpn_provider_get_string(provider,
- "OpenConnect.Password");
- if (!username || !password || !g_strcmp0(username, "-") ||
- !g_strcmp0(password, "-")) {
- err = -EACCES;
- goto done;
+ if (opt) {
+ if (password && g_strcmp0(password, "-")) {
+ opt->_value = strdup(password);
+ data->tried_passphrase = true;
+ form_data->status = OC_FORM_RESULT_OK;
+ goto out;
+ } else {
+ if (data->tried_passphrase) {
+ vpn_provider_add_error(data->provider,
+ VPN_PROVIDER_ERROR_AUTH_FAILED);
+ clear_provider_credentials(
+ data->provider,
+ true);
+ }
+ request_input_credentials_full(data,
+ request_input_pkcs_reply,
+ form_data);
+ return G_SOURCE_REMOVE;
+ }
}
- connman_task_add_argument(task, "--user", username);
- connman_task_add_argument(task, "--passwd-on-stdin", NULL);
- break;
+ /* fall-through */
+
+ /*
+ * In case of public key, reaching here means that the
+ * passphrase previously provided was incorrect.
+ */
case OC_CONNECT_PUBLICKEY:
- certificate = vpn_provider_get_string(provider,
- "OpenConnect.ClientCert");
- private_key = vpn_provider_get_string(provider,
- "OpenConnect.UserPrivateKey");
+ data->err = -EACCES;
+ clear_provider_credentials(data->provider, true);
- if (!certificate || !private_key) {
- err = -EACCES;
- goto done;
- }
+ /* fall-through */
+ default:
+ form_data->status = OC_FORM_RESULT_ERR;
+ goto out;
+ }
- connman_task_add_argument(task, "--certificate", certificate);
- connman_task_add_argument(task, "--sslkey", private_key);
- break;
- case OC_CONNECT_PKCS:
- certificate = vpn_provider_get_string(provider,
- "OpenConnect.PKCSClientCert");
- if (!certificate) {
- err = -EACCES;
- goto done;
+ /*
+ * Form values are released with free(), so always use strdup()
+ * instead of g_strdup()
+ */
+ for (opt = form_data->form->opts; opt; opt = opt->next) {
+ if (opt->flags & OC_FORM_OPT_IGNORE)
+ continue;
+
+ if (opt->type == OC_FORM_OPT_TEXT &&
+ g_str_has_prefix(opt->name, "user")) {
+ const char *user = vpn_provider_get_string(
+ data->provider,
+ "OpenConnect.Username");
+ if (user)
+ opt->_value = strdup(user);
+ } else if (opt->type == OC_FORM_OPT_PASSWORD) {
+ const char *pass = vpn_provider_get_string(
+ data->provider,
+ "OpenConnect.Password");
+ if (pass)
+ opt->_value = strdup(pass);
}
+ }
- connman_task_add_argument(task, "--certificate", certificate);
+ form_data->status = OC_FORM_RESULT_OK;
- password = vpn_provider_get_string(data->provider,
- "OpenConnect.PKCSPassword");
- /* Add password only if it is has been set */
- if (!password || !g_strcmp0(password, "-"))
- break;
+out:
+ form_data->processed = true;
+ g_cond_signal(&form_data->cond);
+ g_mutex_unlock(&form_data->mutex);
+
+ return G_SOURCE_REMOVE;
+}
+
+static int oc_process_auth_form(void *user_data, struct oc_auth_form *form)
+{
+ struct process_form_data data = { .form = form,
+ .data = user_data,
+ .processed = false };
+
+ DBG("");
+
+ g_cond_init(&data.cond);
+ g_mutex_init(&data.mutex);
+
+ g_mutex_lock(&data.mutex);
+ g_idle_add(process_auth_form, &data);
+
+ while (!data.processed)
+ g_cond_wait(&data.cond, &data.mutex);
+
+ g_mutex_unlock(&data.mutex);
+
+ g_mutex_clear(&data.mutex);
+ g_cond_clear(&data.cond);
+
+ return data.status;
+}
+
+static gboolean authenticated(void *user_data)
+{
+ struct oc_private_data *data = user_data;
+ int rv = GPOINTER_TO_INT(g_thread_join(data->cookie_thread));
+
+ DBG("");
+
+ data->cookie_thread = NULL;
+
+ if (rv == 0)
+ rv = run_connect(data, openconnect_get_cookie(data->vpninfo));
+ else if (rv < 0)
+ clear_provider_credentials(data->provider, true);
+
+ openconnect_vpninfo_free(data->vpninfo);
+ data->vpninfo = NULL;
+
+ if (rv != -EINPROGRESS) {
+ oc_connect_done(data, data->err ? data->err : rv);
+ free_private_data(data);
+ }
- connman_task_add_argument(task, "--passwd-on-stdin", NULL);
+ return G_SOURCE_REMOVE;
+}
+
+static void *obtain_cookie_thread(void *user_data)
+{
+ struct oc_private_data *data = user_data;
+ int ret;
+
+ DBG("%p", data->vpninfo);
+
+ ret = openconnect_obtain_cookie(data->vpninfo);
+
+ g_idle_add(authenticated, data);
+
+ return GINT_TO_POINTER(ret);
+}
+
+static int authenticate(struct oc_private_data *data)
+{
+ const char *cert = NULL;
+ const char *key = NULL;
+ const char *urlpath;
+ const char *vpnhost;
+
+ DBG("");
+
+ switch (data->connect_type) {
+ case OC_CONNECT_PKCS:
+ cert = vpn_provider_get_string(data->provider,
+ "OpenConnect.PKCSClientCert");
break;
+ case OC_CONNECT_PUBLICKEY:
+ cert = vpn_provider_get_string(data->provider,
+ "OpenConnect.ClientCert");
+ key = vpn_provider_get_string(data->provider,
+ "OpenConnect.UserPrivateKey");
+ break;
+
+ case OC_CONNECT_USERPASS:
+ case OC_CONNECT_COOKIE_WITH_USERPASS:
+ break;
+
+ default:
+ return -EINVAL;
}
- vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost");
+ openconnect_init_ssl();
+ data->vpninfo = openconnect_vpninfo_new("ConnMan VPN Agent",
+ oc_validate_peer_cert,
+ NULL,
+ oc_process_auth_form,
+ oc_progress,
+ data);
+
+ /* Replicating how openconnect's --usergroup argument works */
+ urlpath = vpn_provider_get_string(data->provider,
+ "OpenConnect.Usergroup");
+ if (urlpath)
+ openconnect_set_urlpath(data->vpninfo, urlpath);
+
+ if (vpn_provider_get_boolean(data->provider,
+ "OpenConnect.DisableIPv6", false))
+ openconnect_disable_ipv6(data->vpninfo);
+
+ vpnhost = vpn_provider_get_string(data->provider,
+ "OpenConnect.VPNHost");
if (!vpnhost || !*vpnhost)
- vpnhost = vpn_provider_get_string(provider, "Host");
+ vpnhost = vpn_provider_get_string(data->provider, "Host");
- task_append_config_data(provider, task);
+ openconnect_set_hostname(data->vpninfo, vpnhost);
+
+ if (cert)
+ openconnect_set_client_cert(data->vpninfo, cert, key);
+
+ data->fd_cmd = openconnect_setup_cmd_pipe(data->vpninfo);
/*
- * To clarify complex situation, if cookie is expected to be printed
- * to stdout all other output must go to syslog. But with PKCS all
- * output must be caught in order to get message about file decryption
- * error. For this reason, the mode has to be interactive as well.
+ * openconnect_obtain_cookie blocks, so run it in background thread
+ * instead
*/
- switch (data->connect_type) {
- case OC_CONNECT_COOKIE:
- /* fall through */
- case OC_CONNECT_COOKIE_WITH_USERPASS:
- /* fall through */
- case OC_CONNECT_USERPASS:
- /* fall through */
- case OC_CONNECT_PUBLICKEY:
- connman_task_add_argument(task, "--syslog", NULL);
+ data->cookie_thread = g_thread_try_new("obtain_cookie",
+ obtain_cookie_thread,
+ data, NULL);
+
+ if (!data->cookie_thread)
+ return -EIO;
+
+ return -EINPROGRESS;
+}
- setting = vpn_provider_get_boolean(provider,
+static int run_connect(struct oc_private_data *data, const char *cookie)
+{
+ struct vpn_provider *provider;
+ struct connman_task *task;
+ const char *vpnhost;
+ int fd_err;
+ int err = 0;
+ bool allow_self_signed;
+ const char *server_cert;
+
+ if (!data || !cookie)
+ return -EINVAL;
+
+ provider = data->provider;
+ task = data->task;
+
+ server_cert = vpn_provider_get_string(provider,
+ "OpenConnect.ServerCert");
+ allow_self_signed = vpn_provider_get_boolean(provider,
"OpenConnect.AllowSelfSignedCert",
false);
- setting_str = vpn_provider_get_string(provider,
- "OpenConnect.ServerCert");
- /*
- * Run in interactive mode if self signed certificates are
- * allowed and there is no set server SHA1 fingerprint.
- */
- if (setting_str || !setting)
- connman_task_add_argument(task, "--non-inter", NULL);
- else
- data->interactive = true;
- break;
- case OC_CONNECT_PKCS:
- data->interactive = true;
- break;
- }
+ DBG("provider %p task %p", provider, task);
+
+ connman_task_add_argument(task, "--cookie-on-stdin", NULL);
+
+ vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost");
+ if (!vpnhost || !*vpnhost)
+ vpnhost = vpn_provider_get_string(provider, "Host");
+
+ task_append_config_data(provider, task);
connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script");
connman_task_add_argument(task, (char *)vpnhost, NULL);
- err = connman_task_run(task, oc_died, data, &data->fd_in, use_stdout ?
- &fd_out : NULL, &fd_err);
+ err = connman_task_run(task, oc_died, data, &data->fd_in,
+ NULL, &fd_err);
if (err < 0) {
err = -EIO;
goto done;
}
- switch (data->connect_type) {
- case OC_CONNECT_COOKIE:
- if (write_data(data->fd_in, vpncookie) != 0) {
- connman_error("openconnect failed to take cookie on "
- "stdin");
- err = -EIO;
- }
-
- break;
- case OC_CONNECT_USERPASS:
- if (write_data(data->fd_in, password) != 0) {
- connman_error("openconnect failed to take password on "
- "stdin");
- err = -EIO;
- }
-
- break;
- case OC_CONNECT_COOKIE_WITH_USERPASS:
- if (!vpncookie || !g_strcmp0(vpncookie, "-")) {
- if (write_data(data->fd_in, password) != 0) {
- connman_error("openconnect failed to take "
- "password on stdin");
- err = -EIO;
- }
- } else {
- if (write_data(data->fd_in, vpncookie) != 0) {
- connman_error("openconnect failed to take "
- "cookie on stdin");
- err = -EIO;
- }
- }
-
- break;
- case OC_CONNECT_PUBLICKEY:
- break;
- case OC_CONNECT_PKCS:
- if (!password || !g_strcmp0(password, "-"))
- break;
+ if (write_data(data->fd_in, cookie) != 0) {
+ connman_error("openconnect failed to take cookie on "
+ "stdin");
+ err = -EIO;
+ }
- if (write_data(data->fd_in, password) != 0) {
- connman_error("openconnect failed to take PKCS "
- "pass phrase on stdin");
+ if (!server_cert || !allow_self_signed) {
+ if (write_data(data->fd_in,
+ (allow_self_signed ? "yes" : "no"))) {
+ connman_error("openconnect failed to take certificate "
+ "acknowledgement on stdin");
err = -EIO;
}
-
- break;
}
if (err) {
- if (fd_out > 0)
- close(fd_out);
-
- if (fd_err > 0)
+ if (fd_err >= 0)
close(fd_err);
goto done;
err = -EINPROGRESS;
- if (use_stdout) {
- data->out_ch = g_io_channel_unix_new(fd_out);
-
- /* Use ASCII encoding only */
- if (g_io_channel_set_encoding(data->out_ch, NULL, NULL) !=
- G_IO_STATUS_NORMAL) {
- close_io_channel(data, data->out_ch);
- err = -EIO;
- } else {
- data->out_ch_id = g_io_add_watch(data->out_ch,
- G_IO_IN | G_IO_ERR | G_IO_HUP,
- (GIOFunc)io_channel_out_cb,
- data);
- }
- }
-
data->err_ch = g_io_channel_unix_new(fd_err);
/* Use ASCII encoding only */
}
done:
- clear_provider_credentials(data->provider);
+ clear_provider_credentials(data->provider, err != -EINPROGRESS);
return err;
}
connman_info("provider %p", data->provider);
- if (!reply)
+ if (!reply) {
+ err = ENOENT;
goto err;
+ }
err = vpn_agent_check_and_process_reply_error(reply, data->provider,
data->task, data->cb, data->user_data);
goto out;
}
- if (!vpn_agent_check_reply_has_dict(reply))
+ if (!vpn_agent_check_reply_has_dict(reply)) {
+ err = ENOENT;
goto err;
+ }
dbus_message_iter_init(reply, &iter);
dbus_message_iter_recurse(&iter, &dict);
switch (data->connect_type) {
case OC_CONNECT_COOKIE:
- if (!cookie)
+ if (!cookie) {
+ err = EACCES;
goto err;
+ }
break;
case OC_CONNECT_USERPASS:
/* fall through */
case OC_CONNECT_COOKIE_WITH_USERPASS:
- if (!username || !password)
+ if (!username || !password) {
+ err = EACCES;
goto err;
+ }
break;
case OC_CONNECT_PUBLICKEY:
break; // This should not be reached.
case OC_CONNECT_PKCS:
- if (!pkcspassword)
+ if (!pkcspassword) {
+ err = EACCES;
goto err;
+ }
break;
}
- err = run_connect(data);
+ if (cookie)
+ err = run_connect(data, cookie);
+ else
+ err = authenticate(data);
+
if (err != -EINPROGRESS)
goto err;
return;
err:
- oc_connect_done(data, EACCES);
+ oc_connect_done(data, err);
out:
free_private_data(data);
}
-static int request_input_credentials(struct oc_private_data *data,
- request_input_reply_cb_t cb)
+static int request_input_credentials_full(
+ struct oc_private_data *data,
+ request_input_reply_cb_t cb,
+ void *user_data)
{
DBusMessage *message;
const char *path;
/*
* For backwards compatibility add OpenConnect.ServerCert and
- * OpenConnect.VPNHost as madnatory only in the default authentication
+ * OpenConnect.VPNHost as mandatory only in the default authentication
* mode. Otherwise. add the fields as informational. These should be
* set in provider settings and not to be queried with every connection
* attempt.
request_input_append_informational,
"OpenConnect.PKCSClientCert");
+ /* Do not allow to store or retrieve the encrypted PKCS pass */
+ vpn_agent_append_allow_credential_storage(&dict, false);
+ vpn_agent_append_allow_credential_retrieval(&dict, false);
+
+ /*
+ * Indicate to keep credentials, the PKCS password should not
+ * affect the credential storing.
+ */
+ vpn_agent_append_keep_credentials(&dict, true);
+
request_input_append_to_dict(data->provider, &dict,
request_input_append_password,
"OpenConnect.PKCSPassword");
connman_dbus_dict_close(&iter, &dict);
err = connman_agent_queue_message(data->provider, message,
- connman_timeout_input_request(), cb, data, agent);
+ connman_timeout_input_request(), cb, user_data, agent);
dbus_message_unref(message);
return -EINPROGRESS;
}
+static int request_input_credentials(struct oc_private_data *data,
+ request_input_reply_cb_t cb)
+{
+ return request_input_credentials_full(data, cb, data);
+}
+
static enum oc_connect_type get_authentication_type(
struct vpn_provider *provider)
{
const char *dbus_sender, void *user_data)
{
struct oc_private_data *data;
- const char *vpncookie;
+ const char *vpncookie = NULL;
const char *certificate;
const char *username;
const char *password;
break;
}
- return run_connect(data);
+ if (vpncookie && g_strcmp0(vpncookie, "-"))
+ return run_connect(data, vpncookie);
+ return authenticate(data);
request_input:
err = request_input_credentials(data, request_input_credentials_reply);
static void oc_disconnect(struct vpn_provider *provider)
{
+ struct oc_private_data *data;
+
connman_info("provider %p", provider);
if (!provider)
* agent request to avoid having multiple ones visible.
*/
connman_agent_cancel(provider);
+
+ data = vpn_provider_get_plugin_data(provider);
+
+ if (!data)
+ return;
+
+ if (data->cookie_thread) {
+ char cmd = OC_CMD_CANCEL;
+ int w = write(data->fd_cmd, &cmd, 1);
+ if (w != 1)
+ DBG("Write failed, might be leaking a thread");
+ }
+
}
static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile)
switch (exit_code) {
case 2:
/* Cookie has failed */
- clear_provider_credentials(provider);
+ clear_provider_credentials(provider, false);
return VPN_PROVIDER_ERROR_LOGIN_FAILED;
case 1:
/* fall through */
}
static int oc_route_env_parse(struct vpn_provider *provider, const char *key,
- int *family, unsigned long *idx, enum vpn_provider_route_type *type)
+ int *family, unsigned long *idx,
+ enum vpn_provider_route_type *type)
{
char *end;
const char *start;
#include "../vpn-agent.h"
#include "vpn.h"
-#include "../vpn.h"
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
{ "OpenVPN.ConfigFile", "--config", 1 },
{ "OpenVPN.DeviceType", NULL, 1 },
{ "OpenVPN.Verb", "--verb", 1 },
+ { "OpenVPN.Ping", "--ping", 1},
+ { "OpenVPN.PingExit", "--ping-exit", 1},
+ { "OpenVPN.RemapUsr1", "--remap-usr1", 1},
};
struct ov_private_data {
connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway);
connman_ipaddress_set_peer(ipaddress, peer);
+ connman_ipaddress_set_p2p(ipaddress, true);
vpn_provider_set_ipaddress(provider, ipaddress);
if (nameserver_list) {
connman_task_add_argument(task, "--ifconfig-noexec", NULL);
/*
- * Disable client restarts because we can't handle this at the
- * moment. The problem is that when OpenVPN decides to switch
+ * Disable client restarts with TCP because we can't handle this at
+ * the moment. The problem is that when OpenVPN decides to switch
* from CONNECTED state to RECONNECTING and then to RESOLVE,
* it is not possible to do a DNS lookup. The DNS server is
* not accessible through the tunnel anymore and so we end up
* trying to resolve the OpenVPN servers address.
- */
- connman_task_add_argument(task, "--ping-restart", "0");
-
- /*
+ *
* Disable connetion retrying when OpenVPN is connected over TCP.
* With TCP OpenVPN attempts to handle reconnection silently without
* reporting the error back when establishing a connection or
* including DNS.
*/
option = vpn_provider_get_string(provider, "OpenVPN.Proto");
- if (option && g_str_has_prefix(option, "tcp"))
+ if (option && g_str_has_prefix(option, "tcp")) {
+ option = vpn_provider_get_string(provider, "OpenVPN.PingExit");
+ if (!option)
+ connman_task_add_argument(task, "--ping-restart", "0");
+
connman_task_add_argument(task, "--connect-retry-max", "1");
+ /* Apply defaults for --ping and --ping-exit only with UDP protocol. */
+ } else {
+ /* Apply default of 10 second interval for ping if omitted. */
+ option = vpn_provider_get_string(provider, "OpenVPN.Ping");
+ if (!option)
+ connman_task_add_argument(task, "--ping", "10");
+
+ /* Apply default of 60 seconds for ping exit if omitted. */
+ option = vpn_provider_get_string(provider, "OpenVPN.PingExit");
+ if (!option)
+ connman_task_add_argument(task, "--ping-exit", "60");
+ }
err = connman_task_run(task, ov_died, data, NULL, NULL, NULL);
if (err < 0) {
enum {
OPT_STRING = 1,
OPT_BOOL = 2,
+ OPT_PPTP_ONLY = 3,
};
struct {
const char *cm_opt;
const char *pptp_opt;
- const char *vpnc_default;
+ const char *pptp_default;
int type;
} pptp_options[] = {
{ "PPTP.User", "user", NULL, OPT_STRING },
+ { "PPTP.IdleWait", "--idle-wait", NULL, OPT_PPTP_ONLY},
+ { "PPTP.MaxEchoWait", "--max-echo-wait", NULL, OPT_PPTP_ONLY},
{ "PPPD.EchoFailure", "lcp-echo-failure", "0", OPT_STRING },
{ "PPPD.EchoInterval", "lcp-echo-interval", "0", OPT_STRING },
{ "PPPD.Debug", "debug", NULL, OPT_STRING },
connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
gateway);
+ connman_ipaddress_set_p2p(ipaddress, true);
vpn_provider_set_ipaddress(provider, ipaddress);
vpn_provider_set_nameservers(provider, nameservers);
vpn_provider_connect_cb_t cb, void *user_data,
const char *username, const char *password)
{
- const char *opt_s, *host;
+ GString *pptp_opt_s;
+ const char *opt_s;
+ const char *host;
char *str;
int err, i;
DBG("username %s password %p", username, password);
host = vpn_provider_get_string(provider, "Host");
- str = g_strdup_printf("%s %s --nolaunchpppd --loglevel 2",
- PPTP, host);
- if (!str) {
- connman_error("can not allocate memory");
- err = -ENOMEM;
- goto done;
- }
- connman_task_add_argument(task, "pty", str);
- g_free(str);
+ /* Create PPTP options for pppd "pty" */
+ pptp_opt_s = g_string_new(NULL);
+ g_string_append_printf(pptp_opt_s, "%s %s --nolaunchpppd --loglevel 2",
+ PPTP, host);
connman_task_add_argument(task, "nodetach", NULL);
connman_task_add_argument(task, "lock", NULL);
opt_s = vpn_provider_get_string(provider,
pptp_options[i].cm_opt);
if (!opt_s)
- opt_s = pptp_options[i].vpnc_default;
+ opt_s = pptp_options[i].pptp_default;
if (!opt_s)
continue;
else if (pptp_options[i].type == OPT_BOOL)
pptp_write_bool_option(task,
pptp_options[i].pptp_opt, opt_s);
+ else if (pptp_options[i].type == OPT_PPTP_ONLY)
+ g_string_append_printf(pptp_opt_s, " %s %s",
+ pptp_options[i].pptp_opt, opt_s);
}
+ str = g_string_free(pptp_opt_s, FALSE);
+ connman_task_add_argument(task, "pty", str);
+ g_free(str);
+
connman_task_add_argument(task, "plugin",
SCRIPTDIR "/libppp-plugin.so");
struct vpn_driver_data {
const char *name;
const char *program;
- struct vpn_driver *vpn_driver;
+ const struct vpn_driver *vpn_driver;
struct vpn_provider_driver provider_driver;
};
return ret;
}
-static gboolean is_numeric(const char *str)
+static gid_t get_gid(const char *group_name)
{
- gint i;
-
- if(!str || !(*str))
- return false;
-
- for(i = 0; str[i] ; i++) {
- if(!g_ascii_isdigit(str[i]))
- return false;
- }
-
- return true;
-}
-
-static gint get_gid(const char *group_name)
-{
- gint gid = -1;
struct group *grp;
- if(!group_name || !(*group_name))
- return gid;
-
- if (is_numeric(group_name)) {
- gid_t group_id = (gid_t)g_ascii_strtoull(group_name, NULL, 10);
- grp = getgrgid(group_id);
- } else {
- grp = getgrnam(group_name);
- }
-
+ grp = vpn_util_get_group(group_name);
if (grp)
- gid = grp->gr_gid;
+ return grp->gr_gid;
- return gid;
+ return -1;
}
-static gint get_uid(const char *user_name)
+static uid_t get_uid(const char *user_name)
{
- gint uid = -1;
struct passwd *pw;
- if(!user_name || !(*user_name))
- return uid;
-
- if (is_numeric(user_name)) {
- uid_t user_id = (uid_t)g_ascii_strtoull(user_name, NULL, 10);
- pw = getpwuid(user_id);
- } else {
- pw = getpwnam(user_name);
- }
-
+ pw = vpn_util_get_passwd(user_name);
if (pw)
- uid = pw->pw_uid;
+ return pw->pw_uid;
- return uid;
+ return -1;
}
static gint get_supplementary_gids(gchar **groups, gid_t **gid_list)
static void vpn_task_setup(gpointer user_data)
{
struct vpn_plugin_data *data;
- gint uid;
- gint gid;
+ uid_t uid;
+ gid_t gid;
gid_t *gid_list = NULL;
size_t gid_list_size;
const gchar *user;
vpn_driver_data->vpn_driver->flags & VPN_FLAG_NO_DAEMON) {
ret = vpn_driver_data->vpn_driver->connect(provider,
- NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, cb, dbus_sender, user_data);
if (ret) {
stop_vpn(provider);
goto exist_err;
return 0;
}
-int vpn_register(const char *name, struct vpn_driver *vpn_driver,
+int vpn_register(const char *name, const struct vpn_driver *vpn_driver,
const char *program)
{
struct vpn_driver_data *data;
enum vpn_provider_route_type *type);
};
-int vpn_register(const char *name, struct vpn_driver *driver,
+int vpn_register(const char *name, const struct vpn_driver *driver,
const char *program);
void vpn_unregister(const char *provider_name);
void vpn_died(struct connman_task *task, int exit_code, void *user_data);
#include <stdio.h>
#include <net/if.h>
#include <linux/if_tun.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <fcntl.h>
#include <glib.h>
#include "../vpn.h"
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+#define PID_PATH_ROOT "/var/run/user"
enum {
OPT_STRING = 1,
}
connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway);
+ connman_ipaddress_set_p2p(ipaddress, true);
vpn_provider_set_ipaddress(provider, ipaddress);
g_free(address);
return G_SOURCE_CONTINUE;
}
+static char *create_pid_path(const char *user, const char *group)
+{
+ struct passwd *pwd;
+ struct group *grp;
+ char *uid_str;
+ char *pid_path = NULL;
+ int mode = S_IRWXU|S_IRWXG;
+ gid_t gid;
+
+ if (!user || !*user)
+ return NULL;
+
+ if (vpn_settings_is_system_user(user))
+ return NULL;
+
+ pwd = vpn_util_get_passwd(user);
+ uid_str = g_strdup_printf("%d", pwd->pw_uid);
+
+ grp = vpn_util_get_group(group);
+ gid = grp ? grp->gr_gid : pwd->pw_gid;
+
+ pid_path = g_build_filename(PID_PATH_ROOT, uid_str, "vpnc", "pid",
+ NULL);
+ if (vpn_util_create_path(pid_path, pwd->pw_uid, gid, mode)) {
+ g_free(pid_path);
+ pid_path = NULL;
+ }
+
+ g_free(uid_str);
+
+ return pid_path;
+}
+
static int run_connect(struct vc_private_data *data)
{
struct vpn_provider *provider;
struct connman_task *task;
+ struct vpn_plugin_data *plugin_data;
const char *credentials[] = {"VPNC.IPSec.Secret", "VPNC.Xauth.Username",
"VPNC.Xauth.Password", NULL};
const char *if_name;
const char *option;
+ char *pid_path;
int err;
int fd_in;
int fd_err;
connman_task_add_argument(task, "--ifmode", "tun");
}
+ plugin_data = vpn_settings_get_vpn_plugin_config("vpnc");
+
+ option = vpn_settings_get_binary_user(plugin_data);
+ if (option) {
+ pid_path = create_pid_path(option,
+ vpn_settings_get_binary_group(
+ plugin_data));
+ if (pid_path)
+ connman_task_add_argument(task, "--pid-file",
+ pid_path);
+
+ g_free(pid_path);
+ }
+
connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script");
option = vpn_provider_get_string(provider, "VPNC.Debug");
DBG("provider %p", data->provider);
- if (!reply)
+ if (!reply) {
+ err = ENOENT;
goto err;
+ }
err = vpn_agent_check_and_process_reply_error(reply, data->provider,
data->task, data->cb, data->user_data);
return;
}
- if (!vpn_agent_check_reply_has_dict(reply))
+ if (!vpn_agent_check_reply_has_dict(reply)) {
+ err = ENOENT;
goto err;
+ }
dbus_message_iter_init(reply, &iter);
dbus_message_iter_recurse(&iter, &dict);
dbus_message_iter_next(&dict);
}
- if (!secret || !username || !password)
+ if (!secret || !username || !password) {
+ vpn_provider_indicate_error(data->provider,
+ VPN_PROVIDER_ERROR_AUTH_FAILED);
+ err = EACCES;
goto err;
+ }
- err = run_connect(data);
- if (err != -EINPROGRESS)
+ /* vpn_provider.c:connect_cb() expects positive errors */
+ err = -run_connect(data);
+ if (err != EINPROGRESS)
goto err;
return;
err:
- vc_connect_done(data, EACCES);
+ vc_connect_done(data, err);
}
static int request_input_credentials(struct vc_private_data *data,
#include "vpn.h"
#include "wireguard.h"
+#define DNS_RERESOLVE_TIMEOUT 20
+
+struct wireguard_info {
+ struct wg_device device;
+ struct wg_peer peer;
+ char *endpoint_fqdn;
+ char *port;
+ int reresolve_id;
+};
+
+struct sockaddr_u {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ };
+};
+
static int parse_key(const char *str, wg_key key)
{
unsigned char *buf;
return 0;
}
-static int parse_endpoint(const char *host, const char *port, wg_peer *peer)
+static int parse_endpoint(const char *host, const char *port, struct sockaddr_u *addr)
{
struct addrinfo hints;
struct addrinfo *result, *rp;
return -EINVAL;
}
- memcpy(&peer->endpoint.addr, rp->ai_addr, rp->ai_addrlen);
+ memcpy(addr, rp->ai_addr, rp->ai_addrlen);
freeaddrinfo(result);
return 0;
err = -EINVAL;
}
+ connman_ipaddress_set_p2p(*ipaddress, true);
+
g_strfreev(tokens);
if (err)
connman_ipaddress_free(*ipaddress);
for (i = 0; i < 256; i++) {
data.ifname = g_strdup_printf("wg%d", i);
data.found = false;
- __vpn_ipconfig_foreach(ifname_check_cb, &data);
+ vpn_ipconfig_foreach(ifname_check_cb, &data);
if (!data.found)
return data.ifname;
return NULL;
}
-struct wireguard_info {
- struct wg_device device;
- struct wg_peer peer;
-};
+static bool sockaddr_cmp_addr(struct sockaddr_u *a, struct sockaddr_u *b)
+{
+ if (a->sa.sa_family != b->sa.sa_family)
+ return false;
+
+ if (a->sa.sa_family == AF_INET)
+ return !memcmp(&a->sin, &b->sin, sizeof(struct sockaddr_in));
+ else if (a->sa.sa_family == AF_INET6)
+ return !memcmp(a->sin6.sin6_addr.s6_addr,
+ b->sin6.sin6_addr.s6_addr,
+ sizeof(a->sin6.sin6_addr.s6_addr));
+
+ return false;
+}
+
+static gboolean wg_dns_reresolve_cb(gpointer user_data)
+{
+ struct wireguard_info *info = user_data;
+ struct sockaddr_u addr;
+ int err;
+
+ DBG("");
+
+ err = parse_endpoint(info->endpoint_fqdn,
+ info->port, &addr);
+ if (err)
+ return TRUE;
+
+ if (sockaddr_cmp_addr(&addr,
+ (struct sockaddr_u *)&info->peer.endpoint.addr))
+ return TRUE;
+
+ if (addr.sa.sa_family == AF_INET)
+ memcpy(&info->peer.endpoint.addr, &addr.sin,
+ sizeof(info->peer.endpoint.addr4));
+ else
+ memcpy(&info->peer.endpoint.addr, &addr.sin6,
+ sizeof(info->peer.endpoint.addr6));
+
+ DBG("Endpoint address has changed, udpate WireGuard device");
+ err = wg_set_device(&info->device);
+ if (err)
+ DBG("Failed to update Endpoint address for WireGuard device %s",
+ info->device.name);
+
+ return TRUE;
+}
static int wg_connect(struct vpn_provider *provider,
struct connman_task *task, const char *if_name,
option = "51820";
gateway = vpn_provider_get_string(provider, "Host");
- err = parse_endpoint(gateway, option, &info->peer);
+ err = parse_endpoint(gateway, option,
+ (struct sockaddr_u *)&info->peer.endpoint.addr);
if (err)
goto done;
+ info->endpoint_fqdn = g_strdup(gateway);
+ info->port = g_strdup(option);
+
option = vpn_provider_get_string(provider, "WireGuard.Address");
if (!option) {
DBG("Missing WireGuard.Address configuration");
err = -ENOENT;
goto done;
}
- stpncpy(info->device.name, ifname, sizeof(info->device.name));
+ stpncpy(info->device.name, ifname, sizeof(info->device.name) - 1);
g_free(ifname);
err = wg_add_device(info->device.name);
connman_ipaddress_free(ipaddress);
+ if (!err)
+ info->reresolve_id =
+ g_timeout_add_seconds(DNS_RERESOLVE_TIMEOUT,
+ wg_dns_reresolve_cb, info);
+
return err;
}
info = vpn_provider_get_plugin_data(provider);
if (!info)
return;
+
+ if (info->reresolve_id > 0)
+ g_source_remove(info->reresolve_id);
+
vpn_provider_set_plugin_data(provider, NULL);
wg_del_device(info->device.name);
+ g_free(info->endpoint_fqdn);
+ g_free(info->port);
g_free(info);
}
host = get_string(config_provider, "Host");
domain = get_string(config_provider, "Domain");
- if (host && domain) {
+ if (host) {
char *id = __vpn_provider_create_identifier(host, domain);
struct vpn_provider *provider;
DBG("provider identifier %s", id);
} else {
- DBG("invalid values host %s domain %s", host, domain);
+ DBG("invalid configuration: no host specified");
err = -EINVAL;
goto err;
}
return strlist;
}
+
+bool __vpn_config_get_boolean(GKeyFile *key_file, const char *group_name,
+ const char *key, bool default_value)
+{
+ GError *error = NULL;
+ bool val;
+
+ val = g_key_file_get_boolean(key_file, group_name, key, &error);
+ if (error) {
+ g_error_free(error);
+ return default_value;
+ }
+
+ return val;
+}
return ipdevice->flags;
}
-void __vpn_ipconfig_foreach(void (*function) (int index,
+void vpn_ipconfig_foreach(void (*function) (int index,
void *user_data), void *user_data)
{
GList *list, *keys;
if (family == AF_INET)
return connman_inet_set_address(ipconfig->index,
- ipconfig->address);
+ ipconfig->address);
else if (family == AF_INET6)
return connman_inet_set_ipv6_address(ipconfig->index,
ipconfig->address);
return NULL;
}
+ connman_ipaddress_set_p2p(ipv6config->address, true);
+
ipv6config->system = connman_ipaddress_alloc(AF_INET6);
+ connman_ipaddress_set_p2p(ipv6config->system, true);
DBG("ipconfig %p", ipv6config);
return NULL;
}
+ connman_ipaddress_set_p2p(ipconfig->address, true);
+
ipconfig->system = connman_ipaddress_alloc(AF_INET);
+ connman_ipaddress_set_p2p(ipconfig->system, true);
DBG("ipconfig %p", ipconfig);
static GHashTable *provider_hash;
static GSList *driver_list;
static int configuration_count;
-static bool handle_routes;
struct vpn_route {
int family;
char *host;
char *domain;
int family;
+ bool do_split_routing;
GHashTable *routes;
struct vpn_provider_driver *driver;
void *driver_data;
void *plugin_data;
unsigned int auth_error_counter;
unsigned int conn_error_counter;
+ unsigned int signal_watch;
};
static void append_properties(DBusMessageIter *iter,
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;
&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;
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)
{
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;
+
+ dbus_message_iter_get_basic(value, &split_routing);
- if (!handle_routes)
- send_routes(provider, provider->user_routes,
- "UserRoutes");
+ DBG("property %s value %s ", name, bool2str(split_routing));
+ vpn_provider_set_boolean(provider, name, split_routing, false);
} else {
const char *str;
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;
}
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) {
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);
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,
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;
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);
"Host", provider->host);
g_key_file_set_string(keyfile, provider->identifier,
"VPN.Domain", provider->domain);
+
if (provider->user_networks) {
gchar **networks;
gsize network_count;
GHashTableIter hash;
gpointer value, key;
dbus_bool_t immutable;
+ dbus_bool_t split_routing;
connman_dbus_dict_open(iter, &dict);
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);
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);
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)
{
__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);
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;
static int connection_register(struct vpn_provider *provider)
{
+ char *connmand_vpn_path;
+
DBG("provider %p path %s", provider, provider->path);
if (provider->path)
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;
}
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);
return NULL;
}
- if (provider_register(provider) == 0)
+ if (!provider_register(provider)) {
connection_register(provider);
+ connection_added_signal(provider);
+ }
}
+
return provider;
}
{
char *ident;
- ident = g_strdup_printf("%s_%s", host, domain);
- if (!ident)
- return NULL;
+ if (domain)
+ ident = g_strdup_printf("%s_%s", host, domain);
+ else
+ ident = g_strdup_printf("%s", host);
provider_dbus_ident(ident);
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);
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);
dbus_message_iter_next(&array);
}
- if (!host || !domain)
+ if (!host)
return -EINVAL;
DBG("Type %s name %s networks %p", type, name, networks);
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);
networks_str = get_string(settings, "Networks");
networks = parse_user_networks(networks_str);
- if (!host || !domain) {
+ if (!host) {
err = -EINVAL;
goto fail;
}
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;
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)
{
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;
}
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;
}
}
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;
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",
const char *key);
bool vpn_provider_get_string_immutable(struct vpn_provider *provider,
const char *key);
+int vpn_provider_set_boolean(struct vpn_provider *provider, const char *key,
+ bool value,
+ bool force_change);
bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key,
bool default_value);
const char *vpn_provider_get_name(struct vpn_provider *provider);
const char *vpn_provider_get_host(struct vpn_provider *provider);
const char *vpn_provider_get_path(struct vpn_provider *provider);
+const char *vpn_provider_get_ident(struct vpn_provider *provider);
unsigned int vpn_provider_get_authentication_errors(
struct vpn_provider *provider);
rtnl_list = g_slist_insert_sorted(rtnl_list, rtnl,
compare_priority);
- __vpn_ipconfig_foreach(trigger_rtnl, rtnl);
+ vpn_ipconfig_foreach(trigger_rtnl, rtnl);
return 0;
}
* ConnMan VPN daemon settings
*
* Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
- * Copyright (C) 2018-2019 Jolla Ltd. All rights reserved.
+ * Copyright (C) 2018-2020 Jolla Ltd. All rights reserved.
* Contact: jussi.laakkonen@jolla.com
*
* This program is free software; you can redistribute it and/or modify
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <sys/types.h>
#include <connman/log.h>
char *binary_user;
char *binary_group;
char **binary_supplementary_groups;
+ char **system_binary_users;
} connman_vpn_settings = {
.timeout_inputreq = DEFAULT_INPUT_REQUEST_TIMEOUT,
.binary_user = NULL,
.binary_group = NULL,
.binary_supplementary_groups = NULL,
+ .system_binary_users = NULL,
};
struct vpn_plugin_data {
GHashTable *plugin_hash = NULL;
+bool vpn_settings_is_system_user(const char *user)
+{
+ struct passwd *pwd;
+ struct passwd *system_pwd;
+ int i;
+
+ /*
+ * The username is not set = override should not be used. This is the
+ * case after the override is reset.
+ */
+ if (!user)
+ return true;
+
+ DBG("check user \"%s\"", user);
+
+ /*
+ * Ignore errors if no entry was found. Treat as system user to
+ * prevent using an invalid override.
+ */
+ pwd = vpn_util_get_passwd(user);
+ if (!pwd)
+ return true;
+
+ if (!connman_vpn_settings.system_binary_users) {
+ DBG("no binary users set");
+
+ /*
+ * Check if the user is root, or the uid equals to process
+ * effective uid.
+ */
+ return !pwd->pw_uid || pwd->pw_uid == geteuid();
+ }
+
+ /* Root set as user or the effective user id */
+ if (!pwd->pw_uid || pwd->pw_uid == geteuid())
+ return true;
+
+ for (i = 0; connman_vpn_settings.system_binary_users[i]; i++) {
+ const char *system_user =
+ connman_vpn_settings.system_binary_users[i];
+
+ system_pwd = vpn_util_get_passwd(system_user);
+ if (!system_pwd)
+ continue;
+
+ if (pwd->pw_uid == system_pwd->pw_uid)
+ return true;
+ }
+
+ return false;
+}
+
const char *vpn_settings_get_binary_user(struct vpn_plugin_data *data)
{
if (data && data->binary_user)
connman_vpn_settings.binary_supplementary_groups = get_string_list(
config, VPN_GROUP,
"SupplementaryGroups");
+ connman_vpn_settings.system_binary_users = get_string_list(
+ config, VPN_GROUP,
+ "SystemBinaryUsers");
}
struct vpn_plugin_data *vpn_settings_get_vpn_plugin_config(const char *name)
g_free(connman_vpn_settings.binary_user);
g_free(connman_vpn_settings.binary_group);
g_strfreev(connman_vpn_settings.binary_supplementary_groups);
+ g_strfreev(connman_vpn_settings.system_binary_users);
if (plugin_hash) {
g_hash_table_destroy(plugin_hash);
--- /dev/null
+/*
+ * ConnMan VPN daemon utils
+ *
+ * Copyright (C) 2020 Jolla Ltd. All rights reserved.
+ * Copyright (C) 2020 Open Mobile Platform LLC.
+ * Contact: jussi.laakkonen@jolla.com
+ *
+ * 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
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <glib/gstdio.h>
+
+#include <connman/log.h>
+
+#include "vpn.h"
+
+static bool is_string_digits(const char *str)
+{
+ int i;
+
+ if (!str || !*str)
+ return false;
+
+ for (i = 0; str[i]; i++) {
+ if (!g_ascii_isdigit(str[i]))
+ return false;
+ }
+
+ return true;
+}
+
+static uid_t get_str_id(const char *username)
+{
+ if (!username || !*username)
+ return 0;
+
+ return (uid_t)g_ascii_strtoull(username, NULL, 10);
+}
+
+struct passwd *vpn_util_get_passwd(const char *username)
+{
+ struct passwd *pwd;
+ uid_t uid;
+
+ if (!username || !*username)
+ return NULL;
+
+ if (is_string_digits(username)) {
+ uid = get_str_id(username);
+ pwd = getpwuid(uid);
+ } else {
+ pwd = getpwnam(username);
+ }
+
+ return pwd;
+}
+
+struct group *vpn_util_get_group(const char *groupname)
+{
+ struct group *grp;
+ gid_t gid;
+
+ if (!groupname || !*groupname)
+ return NULL;
+
+ if (is_string_digits(groupname)) {
+ gid = get_str_id(groupname);
+ grp = getgrgid(gid);
+ } else {
+ grp = getgrnam(groupname);
+ }
+
+ return grp;
+}
+
+/*
+ * These prefixes are used for checking if the requested path for
+ * vpn_util_create_path() is acceptable. Allow only prefixes meant for run-time
+ * or temporary use to limit the access to any system resources.
+ *
+ * VPN core and plugins would need to create only temporary dirs for the
+ * run-time use. The requested dirs can be created for a specific user when
+ * running a VPN plugin as a different user and thus, user specific run dir is
+ * allowed and limitation to access any other system dir is restricted.
+ */
+static const char *allowed_prefixes[] = { "/var/run/connman-vpn/",
+ "/var/run/user/", "/tmp/", NULL };
+
+static int is_path_allowed(const char *path)
+{
+ int err = -EPERM;
+ int i;
+
+ if (!path || !*path || !g_path_is_absolute(path))
+ return -EINVAL;
+
+ if (g_strrstr(path, "..") || g_strrstr(path, "./"))
+ return -EPERM;
+
+ for (i = 0; allowed_prefixes[i]; i++) {
+ if (g_str_has_prefix(path, allowed_prefixes[i])) {
+ const char *suffix = path +
+ strlen(allowed_prefixes[i]);
+
+ /*
+ * Don't allow plain prefixes, an additional dir must
+ * be included after the prexix in the requested path.
+ */
+ if (suffix && *suffix != G_DIR_SEPARATOR &&
+ g_strrstr(suffix,
+ G_DIR_SEPARATOR_S)) {
+ DBG("allowed %s, has suffix %s", path, suffix);
+ err = 0;
+ }
+
+ break;
+ }
+ }
+
+ return err;
+}
+
+int vpn_util_create_path(const char *path, uid_t uid, gid_t grp, int mode)
+{
+ mode_t old_umask;
+ char *dir_p;
+ int err;
+
+ err = is_path_allowed(path);
+ if (err)
+ return err;
+
+ dir_p = g_path_get_dirname(path);
+ if (!dir_p)
+ return -ENOMEM;
+
+ err = g_unlink(dir_p);
+ if (err)
+ err = -errno;
+
+ switch (err) {
+ case 0:
+ /* Removed */
+ case -ENOENT:
+ /* Did not exist */
+ break;
+ case -EACCES:
+ /*
+ * Cannot get write access to the containing directory, check
+ * if the path exists.
+ */
+ if (!g_file_test(dir_p, G_FILE_TEST_EXISTS))
+ goto out;
+
+ /* If the dir does not exist new one cannot be created */
+ if (!g_file_test(dir_p, G_FILE_TEST_IS_DIR))
+ goto out;
+
+ /* Do a chmod as the dir exists */
+ /* fallthrough */
+ case -EISDIR:
+ /* Exists as dir, just chmod and change owner */
+ err = g_chmod(dir_p, mode);
+ if (err) {
+ connman_warn("chmod %s failed, err %d", dir_p, err);
+ err = -errno;
+ }
+
+ goto chown;
+ default:
+ /* Any other error that is not handled here */
+ connman_warn("remove %s failed, err %d", dir_p, err);
+ goto out;
+ }
+
+ /* Set dir creation mask to correspond to the mode */
+ old_umask = umask(~mode & 0777);
+
+ DBG("mkdir %s", dir_p);
+ err = g_mkdir_with_parents(dir_p, mode);
+
+ umask(old_umask);
+
+ if (err) {
+ connman_warn("mkdir %s failed, err %d", dir_p, err);
+ err = -errno;
+ goto out;
+ }
+
+chown:
+ if (uid && grp) {
+ err = chown(dir_p, uid, grp);
+ if (err) {
+ err = -errno;
+ connman_warn("chown %s failed for %d/%d, err %d",
+ dir_p, uid, grp, err);
+ }
+ }
+
+out:
+ g_free(dir_p);
+
+ return err;
+}
+
struct connman_ipaddress *__vpn_ipconfig_get_address(struct vpn_ipconfig *ipconfig);
unsigned short __vpn_ipconfig_get_type_from_index(int index);
unsigned int __vpn_ipconfig_get_flags_from_index(int index);
-void __vpn_ipconfig_foreach(void (*function) (int index,
+void vpn_ipconfig_foreach(void (*function) (int index,
void *user_data), void *user_data);
void __vpn_ipconfig_set_local(struct vpn_ipconfig *ipconfig,
const char *address);
int __vpn_provider_set_string_immutable(struct vpn_provider *provider,
const char *key, const char *value);
DBusMessage *__vpn_provider_get_connections(DBusMessage *msg);
-const char *vpn_provider_get_ident(struct vpn_provider *provider);
struct vpn_provider *__vpn_provider_lookup(const char *identifier);
int __vpn_provider_indicate_state(struct vpn_provider *provider,
enum vpn_provider_state state);
int __vpn_provider_remove(const char *path);
int __vpn_provider_delete(struct vpn_provider *provider);
void __vpn_provider_cleanup(void);
-int __vpn_provider_init(bool handle_routes);
+int __vpn_provider_init();
#include "vpn-rtnl.h"
int __vpn_config_init(void);
void __vpn_config_cleanup(void);
-char *__vpn_config_get_string(GKeyFile *key_file,
- const char *group_name, const char *key, GError **error);
-char **__vpn_config_get_string_list(GKeyFile *key_file,
- const char *group_name, const char *key, gsize *length, GError **error);
+char *__vpn_config_get_string(GKeyFile *key_file, const char *group_name,
+ const char *key, GError **error);
+char **__vpn_config_get_string_list(GKeyFile *key_file, const char *group_name,
+ const char *key, gsize *length, GError **error);
+bool __vpn_config_get_boolean(GKeyFile *key_file, const char *group_name,
+ const char *key, bool default_value);
int __vpn_settings_init(const char *file);
void __vpn_settings_cleanup(void);
const char * vpn_settings_get_binary_group(struct vpn_plugin_data *data);
char ** vpn_settings_get_binary_supplementary_groups(
struct vpn_plugin_data *data);
+bool vpn_settings_is_system_user(const char *user);
+
+struct passwd *vpn_util_get_passwd(const char *username);
+struct group *vpn_util_get_group(const char *groupname);
+int vpn_util_create_path(const char *path, uid_t uid, gid_t grp, int mode);