Imported Upstream version 1.40 64/271064/1 upstream/1.40
authorNiraj Kumar Goit <niraj.g@samsung.com>
Tue, 15 Feb 2022 04:53:07 +0000 (10:23 +0530)
committerNiraj Kumar Goit <niraj.g@samsung.com>
Tue, 15 Feb 2022 04:55:05 +0000 (10:25 +0530)
Change-Id: Id3e405d088ee3fb19fd0ca049e1cb7f812b40fca
Signed-off-by: Niraj Kumar Goit <niraj.g@samsung.com>
83 files changed:
.mailmap
AUTHORS
ChangeLog
Makefile.am
Makefile.plugins
README
configure.ac
doc/clock-api.txt
doc/connman-vpn-provider.config.5.in
doc/connman-vpn.8.in
doc/connman.conf.5.in
doc/vpn-config-format.txt
doc/vpn-connection-api.txt
gdbus/watch.c
gdhcp/client.c
gdhcp/common.c
gdhcp/common.h
gdhcp/server.c
include/inet.h
include/ipaddress.h
include/network.h
include/option.h [deleted file]
include/provider.h
include/service.h
include/setting.h
plugins/bluetooth.c
plugins/dundee.c
plugins/ethernet.c
plugins/iwd.c
plugins/neard.c
plugins/ofono.c
plugins/vpn.c
plugins/wifi.c
src/bridge.c
src/clock.c
src/connection.c
src/connman.h
src/dbus.c
src/dhcp.c
src/dhcpv6.c
src/dns-systemd-resolved.c
src/dnsproxy.c
src/inet.c
src/ipaddress.c
src/ipconfig.c
src/iptables.c
src/main.c
src/main.conf
src/network.c
src/peer.c
src/provider.c
src/rtnl.c
src/service.c
src/session.c
src/shared/util.c
src/technology.c
src/tethering.c
src/timeserver.c
src/wispr.c
test/monitor-connman
test/monitor-services
test/p2p-on-supplicant
test/simple-agent
test/test-counter
test/test-session
vpn/connman-vpn.service.in
vpn/main.c
vpn/plugins/l2tp.c
vpn/plugins/openconnect.c
vpn/plugins/openvpn.c
vpn/plugins/pptp.c
vpn/plugins/vpn.c
vpn/plugins/vpn.h
vpn/plugins/vpnc.c
vpn/plugins/wireguard.c
vpn/vpn-config.c
vpn/vpn-ipconfig.c
vpn/vpn-provider.c
vpn/vpn-provider.h
vpn/vpn-rtnl.c
vpn/vpn-settings.c
vpn/vpn-util.c [new file with mode: 0644]
vpn/vpn.h

index 1e374a6..ef44bd6 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -11,3 +11,6 @@ Bing Niu <bing.niu@intel.com>                         <bing.niu@intel.com>
 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>
diff --git a/AUTHORS b/AUTHORS
index e03a071..438d45f 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -159,3 +159,15 @@ Yasser <yasser.toor@gmail.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>
index dedc1fe..69207d2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+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.
index 5971ca9..e5718b1 100644 (file)
@@ -17,7 +17,7 @@ include_HEADERS = include/log.h include/plugin.h \
 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 \
@@ -181,7 +181,7 @@ vpn_connman_vpnd_SOURCES = $(builtin_vpn_sources) $(backtrace_sources) \
                        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@ \
index ab2bbe0..8e32361 100644 (file)
@@ -92,7 +92,9 @@ builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
 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)
@@ -100,8 +102,10 @@ vpn_plugins_openconnect_la_SOURCES = vpn/plugins/openconnect.c
 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
 
diff --git a/README b/README
index e911bc2..b8154e6 100644 (file)
--- a/README
+++ b/README
@@ -444,10 +444,12 @@ Information
 ===========
 
 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
index 5041e69..b55d248 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(connman, 1.38)
+AC_INIT(connman, 1.40)
 
 AC_CONFIG_MACRO_DIR([m4])
 
@@ -82,6 +82,8 @@ if (test "${enable_openconnect}" != "no"); then
                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")
index 6818f5a..a7fdf55 100644 (file)
@@ -85,3 +85,12 @@ Properties   uint64 Time [readonly or readwrite]  [experimental]
 
                        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.
index cea99e6..036b60c 100644 (file)
@@ -105,7 +105,7 @@ MTU of the tunnel.
 .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
index 6130b3b..829c498 100644 (file)
@@ -19,7 +19,6 @@ ConnMan-VPN \- VPN management daemon
 .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
@@ -54,9 +53,6 @@ present, then only debug prints from that source file are printed. Example:
 .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)
index a90c229..2e06b3e 100644 (file)
@@ -167,6 +167,27 @@ transitioned to ONLINE state.
 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.
index 91e2a63..f2adf29 100644 (file)
@@ -38,7 +38,7 @@ Allowed fields:
 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
index ec55788..6e6293e 100644 (file)
@@ -130,7 +130,14 @@ Properties string State [readonly]
                        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]
 
index 447e486..8fa76cd 100644 (file)
@@ -136,38 +136,55 @@ static struct filter_data *filter_data_find(DBusConnection *connection)
        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);
@@ -175,21 +192,23 @@ static gboolean add_match(struct filter_data *data,
                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);
 
@@ -198,9 +217,11 @@ static gboolean remove_match(struct filter_data *data)
                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;
 }
 
index 09dfe5e..c7b85e5 100644 (file)
@@ -1629,12 +1629,12 @@ static void start_request(GDHCPClient *dhcp_client)
                                                        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;
 
@@ -2226,7 +2226,8 @@ static void get_dhcpv6_request(GDHCPClient *dhcp_client,
        }
 }
 
-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;
@@ -2237,7 +2238,7 @@ static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
        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));
@@ -2269,7 +2270,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
 {
        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;
@@ -2297,6 +2298,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                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) {
@@ -2361,7 +2363,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                        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;
        }
@@ -2378,7 +2380,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                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);
 
@@ -2428,9 +2430,9 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
 
                        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);
 
@@ -2438,7 +2440,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                        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);
                        }
index 1d667d1..c8916aa 100644 (file)
@@ -73,18 +73,21 @@ GDHCPOptionType dhcp_get_code_type(uint8_t code)
        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;
 
@@ -115,14 +118,25 @@ uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
                        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];
index 9660231..8f63fd7 100644 (file)
@@ -179,7 +179,7 @@ struct in6_pktinfo {
 };
 #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,
index 85405f1..52ea2a5 100644 (file)
@@ -413,7 +413,7 @@ error:
 }
 
 
-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;
 
@@ -423,7 +423,7 @@ static uint8_t check_packet_type(struct dhcp_packet *packet)
        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;
@@ -651,6 +651,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
        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)) {
@@ -661,12 +662,13 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
        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);
@@ -675,7 +677,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                        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);
 
index fdc2155..9245eef 100644 (file)
@@ -55,7 +55,7 @@ bool connman_inet_compare_ipv6_subnet(int index, const char *host);
 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,
@@ -78,6 +78,14 @@ int connman_inet_ipv6_get_dest_addr(int index, char **dest);
 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
 }
index 3655ca8..652db0f 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef __CONNMAN_IPADDRESS_H
 #define __CONNMAN_IPADDRESS_H
 
+#include <stdbool.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -36,6 +38,8 @@ struct connman_ipaddress;
 
 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,
index 8f87d7c..8f9dd94 100644 (file)
@@ -132,6 +132,8 @@ uint16_t connman_network_get_frequency(struct connman_network *network);
 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);
@@ -161,6 +163,8 @@ struct connman_network_driver {
        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);
diff --git a/include/option.h b/include/option.h
deleted file mode 100644 (file)
index 5e97ed4..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- *
- *  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 */
index b585665..1f12099 100644 (file)
@@ -113,6 +113,10 @@ int connman_provider_set_nameservers(struct connman_provider *provider,
                                        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);
index 4a129b4..8f6b36b 100644 (file)
@@ -94,6 +94,7 @@ enum connman_service_connect_reason {
        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;
index a882021..920e675 100644 (file)
@@ -29,6 +29,8 @@ extern "C" {
 #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);
 
index f759a90..5336103 100644 (file)
@@ -717,8 +717,6 @@ static bool tethering_create(const char *path,
        const char *method;
        bool result;
 
-       DBG("path %s bridge %s", path, bridge);
-
        if (!bridge) {
                g_free(tethering);
                return false;
@@ -730,6 +728,8 @@ static bool tethering_create(const char *path,
                return false;
        }
 
+       DBG("path %s bridge %s", path, bridge);
+
        tethering->technology = technology;
        tethering->bridge = g_strdup(bridge);
        tethering->enable = enabled;
index b5420ac..57571ec 100644 (file)
@@ -488,7 +488,7 @@ static void extract_settings(DBusMessageIter *array,
        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;
 
index ed4208a..6146b1c 100644 (file)
@@ -73,7 +73,7 @@ static int get_vlan_vid(const char *ifname)
                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;
@@ -99,14 +99,16 @@ static int get_dsa_port(const char *ifname)
                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;
index bf6a2c2..4ba107f 100644 (file)
@@ -95,6 +95,8 @@ struct iwd_network {
 
        struct iwd_device *iwdd;
        struct connman_network *network;
+       /* service's autoconnect */
+       bool autoconnect;
 };
 
 struct iwd_known_network {
@@ -106,6 +108,9 @@ struct iwd_known_network {
        char *last_connected_time;
        bool auto_connect;
        int auto_connect_id;
+
+       /* service's autoconnect */
+       bool autoconnect;
 };
 
 struct iwd_station {
@@ -244,7 +249,7 @@ static void cm_network_connect_cb(DBusMessage *message, void *user_data)
                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;
@@ -318,12 +323,157 @@ static int cm_network_disconnect(struct connman_network *network)
        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)
@@ -535,7 +685,7 @@ static void tech_enable_tethering_cb(const DBusError *error, void *user_data)
 {
        struct tech_cb_data *cbd = user_data;
        struct iwd_device *iwdd;
-       struct iwd_ap *iwdap;
+       struct iwd_ap *iwdap = NULL;
 
        DBG("");
 
@@ -616,7 +766,7 @@ static void tech_disable_tethering_cb(const DBusError *error, void *user_data)
 
        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;
        }
@@ -923,6 +1073,7 @@ static void network_property_change(GDBusProxy *proxy, const char *name,
                DBusMessageIter *iter, void *user_data)
 {
        struct iwd_network *iwdn;
+       struct iwd_known_network *iwdkn;
        const char *path;
 
        path = g_dbus_proxy_get_path(proxy);
@@ -942,6 +1093,17 @@ static void network_property_change(GDBusProxy *proxy, const char *name,
                        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);
        }
 }
 
@@ -972,12 +1134,15 @@ static void _update_signal_strength(const char *path, int16_t signal_strength)
 
        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("");
 
@@ -1005,6 +1170,11 @@ static void ordered_networks_cb(DBusMessage *message, void *user_data)
 
                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)
@@ -1012,7 +1182,7 @@ 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");
 }
 
@@ -1020,6 +1190,7 @@ static void station_property_change(GDBusProxy *proxy, const char *name,
                DBusMessageIter *iter, void *user_data)
 {
        struct iwd_station *iwds;
+       struct iwd_device *iwdd;
        const char *path;
 
        path = g_dbus_proxy_get_path(proxy);
@@ -1053,8 +1224,15 @@ static void station_property_change(GDBusProxy *proxy, const char *name,
                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);
        }
@@ -1452,86 +1630,6 @@ static void create_network(GDBusProxy *proxy)
        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)
 {
@@ -1551,8 +1649,31 @@ static void known_network_property_change(GDBusProxy *proxy, const char *name,
 
                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;
        }
 }
 
@@ -1589,11 +1710,10 @@ static void create_know_network(GDBusProxy *proxy)
                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)
index 69586df..45effd4 100644 (file)
@@ -499,7 +499,7 @@ static void register_agent_cb(DBusPendingCall *pending, void *user_data)
        DBusMessage *reply;
 
        if (!dbus_pending_call_get_completed(pending))
-               return;
+               goto out;
 
        register_call = NULL;
 
index 36873d5..f0bd3c5 100644 (file)
@@ -910,7 +910,7 @@ static void extract_ipv4_settings(DBusMessageIter *array,
        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;
@@ -998,7 +998,7 @@ static void extract_ipv6_settings(DBusMessageIter *array,
        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;
 
index 5668c00..d708d1f 100644 (file)
@@ -85,6 +85,7 @@ struct connection_data {
        char *domain;
        char **nameservers;
        bool immutable;
+       bool default_route_set;
 
        GHashTable *server_routes;
        GHashTable *user_routes;
@@ -94,6 +95,7 @@ struct connection_data {
 
        GResolv *resolv;
        guint resolv_id;
+       guint remove_resolv_id;
 };
 
 static int set_string(struct connman_provider *provider,
@@ -151,6 +153,8 @@ static const char *get_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);
 }
@@ -171,10 +175,15 @@ static char *get_ident(const char *path)
 
 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;
@@ -206,7 +215,7 @@ static void resolv_result(GResolvResultStatus status,
         * 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;
 }
@@ -261,14 +270,12 @@ static bool provider_is_connected(struct connection_data *data)
 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;
@@ -288,7 +295,7 @@ static void set_provider_state(struct connection_data *data)
        }
 
        connman_provider_set_state(data->provider, state);
-       return;
+       goto free;
 
 set:
        if (data->cb_data)
@@ -299,6 +306,12 @@ set:
 
        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)
@@ -431,6 +444,7 @@ static int extract_ip(DBusMessageIter *array, int family,
        }
 
        connman_ipaddress_set_peer(data->ip, peer);
+       connman_ipaddress_set_p2p(data->ip, true);
 
        return 0;
 }
@@ -490,8 +504,12 @@ static void connect_reply(DBusPendingCall *call, void *user_data)
        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 "
@@ -769,12 +787,16 @@ static void get_connections_reply(DBusPendingCall *call, void *user_data)
                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);
 
@@ -814,6 +836,7 @@ static void get_connections_reply(DBusPendingCall *call, void *user_data)
 done:
        dbus_message_unref(reply);
 
+out:
        dbus_pending_call_unref(call);
 }
 
@@ -861,12 +884,16 @@ static void remove_connection_reply(DBusPendingCall *call, void *user_data)
        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);
 
@@ -884,6 +911,7 @@ static void remove_connection_reply(DBusPendingCall *call, void *user_data)
 
        dbus_message_unref(reply);
 
+out:
        dbus_pending_call_unref(call);
 }
 
@@ -1010,11 +1038,14 @@ static int disconnect_provider(struct connection_data *data)
        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;
 }
 
@@ -1055,12 +1086,16 @@ static void configuration_create_reply(DBusPendingCall *call, void *user_data)
        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);
 
@@ -1114,6 +1149,7 @@ static void configuration_create_reply(DBusPendingCall *call, void *user_data)
 done:
        dbus_message_unref(reply);
 
+out:
        dbus_pending_call_unref(call);
 }
 
@@ -1461,6 +1497,17 @@ static void set_route(struct connection_data *data, struct vpn_route *route)
                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);
 
@@ -1473,6 +1520,126 @@ static void set_route(struct connection_data *data, struct vpn_route *route)
                                                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,
@@ -1504,28 +1671,30 @@ 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 = {
@@ -1694,12 +1863,52 @@ static int save_route(GHashTable *routes, int family, const char *network,
                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;
@@ -1876,6 +2085,10 @@ static gboolean property_changed(DBusConnection *conn,
                g_free(data->domain);
                data->domain = g_strdup(str);
                connman_provider_set_domain(data->provider, data->domain);
+       } else if (g_str_equal(key, "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) {
@@ -1964,6 +2177,10 @@ static void vpn_disconnect_check_provider(struct connection_data *data)
                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;
        }
 }
 
index fc304e3..e701451 100644 (file)
@@ -49,7 +49,6 @@
 #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
 
@@ -165,6 +166,11 @@ struct wifi_data {
        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;
@@ -477,15 +483,23 @@ static GSupplicantP2PServiceParams *fill_in_peer_service_params(
 
        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;
        }
 
@@ -752,14 +766,15 @@ static void wifi_newlink(unsigned flags, unsigned change, void *user_data)
        }
 
        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;
 }
 
@@ -1178,11 +1193,14 @@ static int get_hidden_connections_params(struct wifi_data *wifi,
                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
@@ -1524,6 +1542,8 @@ static void interface_create_callback(int result,
                                                        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),
@@ -1539,6 +1559,24 @@ static void interface_create_callback(int result,
                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)
@@ -1546,7 +1584,7 @@ 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);
@@ -2220,21 +2258,35 @@ static int network_connect(struct connman_network *network)
 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) {
@@ -2248,6 +2300,7 @@ static void disconnect_callback(int result, GSupplicantInterface *interface,
 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;
 
@@ -2264,10 +2317,16 @@ static int network_disconnect(struct connman_network *network)
 
        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;
 }
@@ -2375,6 +2434,7 @@ static bool handle_wps_completion(GSupplicantInterface *interface,
        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
@@ -2387,9 +2447,13 @@ static bool handle_wps_completion(GSupplicantInterface *interface,
 
                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;
                }
 
@@ -2422,7 +2486,9 @@ static bool handle_4way_handshake_failure(GSupplicantInterface *interface,
 {
        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)
@@ -2544,9 +2610,6 @@ static void interface_state(GSupplicantInterface *interface)
 
                /* 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);
@@ -3350,7 +3413,7 @@ static void sta_remove_callback(int result,
                                        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);
 
index cd2d9ce..df19a6a 100644 (file)
@@ -122,7 +122,8 @@ int __connman_bridge_enable(const char *name, const char *ip_address,
 
        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;
 
index 0fde2c3..906538a 100644 (file)
@@ -173,6 +173,7 @@ static DBusMessage *get_properties(DBusConnection *conn,
 {
        DBusMessage *reply;
        DBusMessageIter array, dict;
+       dbus_bool_t is_synced;
        struct timeval tv;
        const char *str;
 
@@ -210,6 +211,10 @@ static DBusMessage *get_properties(DBusConnection *conn,
        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;
@@ -258,6 +263,7 @@ static DBusMessage *set_property(DBusConnection *conn,
                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);
@@ -283,6 +289,13 @@ static DBusMessage *set_property(DBusConnection *conn,
                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;
 
@@ -362,6 +375,8 @@ static DBusMessage *set_property(DBusConnection *conn,
                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);
 
index 303e992..9d2c696 100644 (file)
@@ -1066,6 +1066,29 @@ int __connman_connection_get_vpn_index(int phy_index)
        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;
index 3bdc0dc..6817608 100644 (file)
@@ -138,8 +138,6 @@ void __connman_log_enable(struct connman_debug_desc *start,
 
 #include <connman/backtrace.h>
 
-#include <connman/option.h>
-
 #include <connman/setting.h>
 
 #include <connman/plugin.h>
@@ -159,7 +157,8 @@ int __connman_inet_modify_address(int cmd, int flags, int index, int family,
                                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);
@@ -302,6 +301,7 @@ struct connman_ipaddress {
        char *peer;
        char *broadcast;
        char *gateway;
+       bool is_p2p; /* P2P connection or VPN, broadcast is excluded. */
 };
 
 struct connman_ipconfig_ops {
@@ -449,7 +449,11 @@ char **__connman_timeserver_system_get();
 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,
@@ -502,6 +506,7 @@ int __connman_connection_gateway_add(struct connman_service *service,
 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);
 
@@ -609,6 +614,7 @@ const char *__connman_network_get_type(struct connman_network *network);
 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);
@@ -656,6 +662,8 @@ void __connman_provider_list(DBusMessageIter *iter, void *user_data);
 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,
@@ -670,6 +678,8 @@ int __connman_provider_init(void);
 
 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);
@@ -720,8 +730,9 @@ int __connman_service_set_mdns(struct connman_service *service,
 
 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);
@@ -737,7 +748,6 @@ int __connman_service_indicate_default(struct connman_service *service);
 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);
@@ -779,6 +789,9 @@ void __connman_service_set_pac(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,
index d80a46c..c454a58 100644 (file)
@@ -246,9 +246,7 @@ dbus_bool_t connman_dbus_property_changed_basic(const char *path,
        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,
@@ -268,9 +266,7 @@ 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,
@@ -291,9 +287,7 @@ 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,
@@ -319,9 +313,7 @@ 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,
@@ -348,9 +340,7 @@ 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,
@@ -377,9 +367,7 @@ 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,
index 42e9f41..2d96c43 100644 (file)
@@ -616,7 +616,7 @@ static int dhcp_initialize(struct connman_dhcp *dhcp)
        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);
index 2d5f8f6..8b68359 100644 (file)
@@ -945,7 +945,7 @@ static void do_dad(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
 
                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;
index 5fe306c..912ab3f 100644 (file)
@@ -106,7 +106,7 @@ static void setlinkdns_append(DBusMessageIter *iter, void *user_data)
 
                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;
@@ -128,7 +128,7 @@ static void setlinkdns_append(DBusMessageIter *iter, void *user_data)
                                        &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;
                        }
index a7bf87a..38dbdd7 100644 (file)
@@ -1767,6 +1767,7 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                        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);
 
@@ -1787,14 +1788,15 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                 * 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;
 
@@ -1802,6 +1804,10 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                 * 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];
@@ -1833,7 +1839,7 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                } 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;
                        }
@@ -1872,6 +1878,10 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                         * 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;
@@ -2877,6 +2887,7 @@ static void dnsproxy_default_changed(struct connman_service *service)
        bool server_enabled = false;
        GSList *list;
        int index;
+       int vpn_index;
 
        DBG("service %p", service);
 
@@ -2893,6 +2904,13 @@ static void dnsproxy_default_changed(struct connman_service *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;
 
@@ -2900,6 +2918,9 @@ static void dnsproxy_default_changed(struct connman_service *service)
                        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;
index 4c34143..df94d1e 100644 (file)
@@ -79,7 +79,8 @@ int __connman_inet_modify_address(int cmd, int flags,
                                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)) +
@@ -94,8 +95,9 @@ int __connman_inet_modify_address(int cmd, int flags,
        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;
@@ -119,17 +121,11 @@ int __connman_inet_modify_address(int cmd, int flags,
        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,
@@ -149,16 +145,27 @@ int __connman_inet_modify_address(int cmd, int flags,
                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,
@@ -189,13 +196,46 @@ done:
        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;
@@ -208,14 +248,7 @@ bool __connman_inet_is_any_addr(const char *address, int family)
                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);
        }
 
@@ -409,18 +442,20 @@ int connman_inet_set_ipv6_address(int index,
        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;
@@ -434,6 +469,7 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
        int err;
        unsigned char prefix_len;
        const char *address, *broadcast, *peer;
+       bool is_p2p;
 
        if (!ipaddress->local)
                return -1;
@@ -442,12 +478,13 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
        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;
@@ -456,10 +493,17 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
        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);
 
@@ -467,7 +511,7 @@ int connman_inet_clear_ipv6_address(int index, const char *address,
                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;
@@ -481,11 +525,13 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
        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);
@@ -494,7 +540,7 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
                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;
@@ -661,7 +707,7 @@ int connman_inet_del_ipv6_network_route(int index, const char *host,
 
        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;
        }
@@ -711,7 +757,7 @@ int connman_inet_add_ipv6_network_route(int index, const char *host,
 
        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;
        }
@@ -727,7 +773,7 @@ int connman_inet_add_ipv6_network_route(int index, const char *host,
         */
 
        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;
@@ -770,7 +816,7 @@ int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway)
 
        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;
        }
@@ -1066,54 +1112,161 @@ 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,
@@ -1134,47 +1287,23 @@ 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)
@@ -2147,98 +2276,156 @@ GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr,
        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)
@@ -2835,58 +3022,30 @@ bool connman_inet_is_ipv6_supported()
        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)
@@ -3020,7 +3179,7 @@ static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex,
 
        ret = inet_pton(family, dst ? dst : gateway, buf);
        g_free(dst);
-       if (ret <= 0)
+       if (ret != 1)
                return -EINVAL;
 
        memset(&rth, 0, sizeof(rth));
@@ -3095,61 +3254,14 @@ int __connman_inet_del_subnet_from_table(uint32_t table_id, int ifindex,
 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,
@@ -3303,7 +3415,7 @@ static int get_nfs_server_ip(const char *cmdline_file, const char *pnp_file,
        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;
index d63d95c..201d834 100644 (file)
@@ -70,10 +70,19 @@ struct connman_ipaddress *connman_ipaddress_alloc(int family)
        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)
@@ -95,7 +104,7 @@ static bool check_ipv6_address(const char *address)
                return false;
 
        err = inet_pton(AF_INET6, address, buf);
-       if (err > 0)
+       if (err == 1)
                return true;
 
        return false;
@@ -223,6 +232,7 @@ connman_ipaddress_copy(struct connman_ipaddress *ipaddress)
        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;
 }
index 915c082..1551826 100644 (file)
@@ -258,153 +258,165 @@ static const char *scope2str(unsigned char scope)
        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()
@@ -696,6 +708,25 @@ static inline gint check_duplicate_address(gconstpointer a, gconstpointer b)
        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)
 {
@@ -718,6 +749,9 @@ int __connman_ipconfig_newaddr(int index, int family, const char *label,
        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);
@@ -1186,6 +1220,15 @@ void __connman_ipconfig_set_prefixlen(struct connman_ipconfig *ipconfig,
        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;
@@ -1217,6 +1260,8 @@ static struct connman_ipconfig *create_ipv6config(int index)
 
        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));
 
@@ -1255,6 +1300,8 @@ struct connman_ipconfig *__connman_ipconfig_create(int index,
 
        ipconfig->system = connman_ipaddress_alloc(AF_INET);
 
+       ipconfig_set_p2p(index, ipconfig);
+
        DBG("ipconfig %p index %d", ipconfig, index);
 
        return ipconfig;
@@ -1451,10 +1498,8 @@ int __connman_ipconfig_address_unset(struct connman_ipconfig *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;
 
@@ -1647,6 +1692,9 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig)
                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 &&
@@ -1657,6 +1705,9 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig)
                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)
@@ -1719,6 +1770,10 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig)
                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;
        }
 
@@ -1728,6 +1783,10 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig)
                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;
        }
 
index 47ea1c2..664b27f 100644 (file)
@@ -2931,7 +2931,7 @@ static int parse_ip_and_mask(const char *str, struct in_addr *ip,
        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;
        }
@@ -2972,7 +2972,7 @@ static int parse_ipv6_and_mask(const char *str, struct in6_addr *ip,
        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;
        }
@@ -3383,7 +3383,7 @@ static int parse_rule_spec(struct connman_iptables *table,
                                        break;
 
                                if (invert)
-                                       ctx->ip->invflags |= IP6T_INV_DSTIP;
+                                       ctx->ipv6->invflags |= IP6T_INV_DSTIP;
                        }
                        
                        break;
index 2371771..6480caa 100644 (file)
 #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
 
@@ -88,6 +96,9 @@ static struct {
        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;
@@ -110,6 +121,9 @@ static struct {
        .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,
@@ -133,6 +147,9 @@ static struct {
 #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"
@@ -141,6 +158,7 @@ static const char *supported_options[] = {
        CONF_BG_SCAN,
        CONF_PREF_TIMESERVERS,
        CONF_AUTO_CONNECT_TECHS,
+       CONF_FAVORITE_TECHS,
        CONF_ALWAYS_CONNECTED_TECHS,
        CONF_PREFERRED_TECHS,
        CONF_FALLBACK_NAMESERVERS,
@@ -155,6 +173,9 @@ static const char *supported_options[] = {
        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,
@@ -280,9 +301,9 @@ static void parse_config(GKeyFile *config)
        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 =
@@ -369,17 +390,17 @@ static void parse_config(GKeyFile *config)
 
        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);
 
@@ -440,10 +461,10 @@ static void parse_config(GKeyFile *config)
 
        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);
 
@@ -458,6 +479,40 @@ static void parse_config(GKeyFile *config)
        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;
@@ -648,7 +703,7 @@ static GOptionEntry options[] = {
        { 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;
@@ -686,6 +741,9 @@ bool connman_setting_get_bool(const char *key)
        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;
 
@@ -698,6 +756,17 @@ bool connman_setting_get_bool(const char *key)
        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))
@@ -919,6 +988,7 @@ int main(int argc, char *argv[])
        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);
index 14965e1..df70e17 100644 (file)
 # 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
index f2ab16b..b12bbc0 100644 (file)
@@ -2112,6 +2112,21 @@ int connman_network_set_wifi_channel(struct connman_network *network,
        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;
index 2102f11..bad5c84 100644 (file)
@@ -154,7 +154,7 @@ static int start_dhcp_server(struct connman_peer *peer)
 
        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;
 
@@ -983,7 +983,10 @@ void connman_peer_add_service(struct connman_peer *peer,
 
        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);
index 7d663e0..e209184 100644 (file)
@@ -53,6 +53,7 @@ void __connman_provider_append_properties(struct connman_provider *provider,
                                                        DBusMessageIter *iter)
 {
        const char *host, *domain, *type;
+       dbus_bool_t split_routing;
 
        if (!provider->driver || !provider->driver->get_property)
                return;
@@ -72,6 +73,12 @@ void __connman_provider_append_properties(struct connman_provider *provider,
        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 *
@@ -435,6 +442,15 @@ const char *__connman_provider_get_ident(struct connman_provider *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)
 {
@@ -597,6 +613,100 @@ void connman_provider_set_autoconnect(struct connman_provider *provider,
                __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;
index dfe6bb6..c9e84da 100644 (file)
@@ -177,6 +177,9 @@ static void read_uevent(struct interface_data *interface)
                } 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;
index 2f497d1..20917a8 100644 (file)
@@ -54,6 +54,9 @@ static unsigned int autoconnect_id = 0;
 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;
@@ -134,8 +137,8 @@ struct connman_service {
        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;
@@ -192,6 +195,8 @@ static const char *reason2string(enum connman_service_connect_reason reason)
                return "auto";
        case CONNMAN_SERVICE_CONNECT_REASON_SESSION:
                return "session";
+       case CONNMAN_SERVICE_CONNECT_REASON_NATIVE:
+               return "native";
        }
 
        return "unknown";
@@ -367,7 +372,26 @@ static enum connman_service_proxy_method string2proxymethod(const char *method)
                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;
@@ -378,6 +402,12 @@ static void set_split_routing(struct connman_service *service, bool value)
                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)
@@ -400,9 +430,10 @@ 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:
@@ -456,9 +487,10 @@ static int service_load(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));
 
                autoconnect = g_key_file_get_boolean(keyfile,
                                service->identifier, "AutoConnect", &error);
@@ -545,8 +577,10 @@ static int service_load(struct connman_service *service)
        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)
@@ -709,9 +743,12 @@ static int service_save(struct connman_service *service)
                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,
@@ -1416,6 +1453,12 @@ static void start_online_check(struct connman_service *service,
                        "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);
@@ -1575,6 +1618,16 @@ static void default_changed(void)
                                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
@@ -1693,6 +1746,8 @@ bool connman_service_set_autoconnect(struct connman_service *service,
        service->autoconnect = autoconnect;
        autoconnect_changed(service);
 
+       connman_network_set_autoconnect(service->network, autoconnect);
+
        return true;
 }
 
@@ -3428,15 +3483,28 @@ static void do_auto_connect(struct connman_service *service,
                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();
 }
@@ -3515,14 +3583,6 @@ int __connman_service_reset_ipconfig(struct connman_service *service,
        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)
 {
@@ -3530,10 +3590,10 @@ void __connman_service_wispr_start(struct connman_service *service,
 
        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);
 }
@@ -3717,9 +3777,7 @@ static DBusMessage *set_property(DBusConnection *conn,
 
                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;
@@ -4166,6 +4224,12 @@ static bool auto_connect_service(GList *services,
                        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)) {
@@ -4495,7 +4559,7 @@ static DBusMessage *connect_service(DBusConnection *conn,
                struct connman_service *temp = list->data;
 
                if (!is_connecting(temp->state) && !is_connected(temp->state))
-                       break;
+                       continue;
 
                if (service == temp)
                        continue;
@@ -4762,27 +4826,22 @@ static void service_schedule_changed(void)
        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) {
                /*
@@ -4793,14 +4852,14 @@ static DBusMessage *move_service(DBusConnection *conn,
                        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);
@@ -4823,7 +4882,7 @@ static DBusMessage *move_service(DBusConnection *conn,
                if (service6 != CONNMAN_IPCONFIG_METHOD_OFF) {
                        if (!check_suitable_state(target->state_ipv6,
                                                        service->state_ipv6))
-                               return __connman_error_invalid_service(msg);
+                               return -EINVAL;
                }
        }
 
@@ -4831,7 +4890,7 @@ static DBusMessage *move_service(DBusConnection *conn,
                if (service4 != CONNMAN_IPCONFIG_METHOD_OFF) {
                        if (!check_suitable_state(target->state_ipv4,
                                                        service->state_ipv4))
-                               return __connman_error_invalid_service(msg);
+                               return -EINVAL;
                }
        }
 
@@ -4839,7 +4898,7 @@ static DBusMessage *move_service(DBusConnection *conn,
                if (target6 != CONNMAN_IPCONFIG_METHOD_OFF) {
                        if (!check_suitable_state(target->state_ipv6,
                                                        service->state_ipv6))
-                               return __connman_error_invalid_service(msg);
+                               return -EINVAL;
                }
        }
 
@@ -4847,7 +4906,7 @@ static DBusMessage *move_service(DBusConnection *conn,
                if (target4 != CONNMAN_IPCONFIG_METHOD_OFF) {
                        if (!check_suitable_state(target->state_ipv4,
                                                        service->state_ipv4))
-                               return __connman_error_invalid_service(msg);
+                               return -EINVAL;
                }
        }
 
@@ -4870,6 +4929,39 @@ static DBusMessage *move_service(DBusConnection *conn,
 
        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);
 }
 
@@ -5190,6 +5282,40 @@ void connman_service_unref_debug(struct connman_service *service,
        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;
@@ -5204,6 +5330,17 @@ static gint service_compare(gconstpointer a, gconstpointer b)
        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;
 
@@ -5596,6 +5733,7 @@ static void report_error_cb(void *user_context, bool retry,
                __connman_service_clear_error(service);
 
                service_complete(service);
+               service_list_sort();
                __connman_connection_update_gateway();
        }
 }
@@ -5667,14 +5805,18 @@ static void request_input_cb(struct connman_service *service,
                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);
@@ -5989,6 +6131,7 @@ static int service_indicate_state(struct connman_service *service)
                                                report_error_cb,
                                                get_dbus_sender(service),
                                                NULL);
+                       goto notifier;
                }
                service_complete(service);
                break;
@@ -5998,6 +6141,7 @@ static int service_indicate_state(struct connman_service *service)
 
        __connman_connection_update_gateway();
 
+notifier:
        if ((old_state == CONNMAN_SERVICE_STATE_ONLINE &&
                        new_state != CONNMAN_SERVICE_STATE_READY) ||
                (old_state == CONNMAN_SERVICE_STATE_READY &&
@@ -6184,11 +6328,13 @@ static gboolean redo_wispr_ipv6(gpointer user_data)
        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;
@@ -6198,6 +6344,22 @@ int __connman_service_online_check_failed(struct connman_service *service,
                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);
 
@@ -6205,12 +6367,10 @@ int __connman_service_online_check_failed(struct connman_service *service,
                                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,
@@ -6567,6 +6727,12 @@ int __connman_service_connect(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);
@@ -6665,36 +6831,6 @@ int __connman_service_disconnect(struct connman_service *service)
        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
@@ -6810,14 +6946,14 @@ static int service_register(struct connman_service *service)
 
        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();
@@ -7222,6 +7358,50 @@ static void update_from_network(struct connman_service *service,
        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
@@ -7231,7 +7411,6 @@ static void update_from_network(struct connman_service *service,
 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;
@@ -7305,37 +7484,7 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
        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);
 
index 2a1dd9a..eeefe3f 100644 (file)
@@ -1804,7 +1804,7 @@ static void session_activate(struct connman_session *session)
                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);
 
index 73c24ae..bda2d2b 100644 (file)
@@ -28,6 +28,7 @@
 #include <stdio.h>
 #include <ctype.h>
 #include <stdarg.h>
+#include <string.h>
 
 #include "src/shared/util.h"
 
index 4e053fc..672d6ea 100644 (file)
@@ -185,10 +185,12 @@ static void technology_save(struct connman_technology *technology)
                                        "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);
@@ -390,6 +392,7 @@ static void technology_load(struct connman_technology *technology)
        gchar *identifier;
        GError *error = NULL;
        bool enable, need_saving = false;
+       char *enc;
 
        DBG("technology %p", technology);
 
@@ -436,8 +439,10 @@ static void technology_load(struct connman_technology *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);
 
index e2687b6..f930a26 100644 (file)
@@ -386,7 +386,8 @@ static void setup_tun_interface(unsigned int flags, unsigned change,
 
        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;
        }
index decca15..feef8e8 100644 (file)
@@ -29,6 +29,7 @@
 #include <stdlib.h>
 #include <gweb/gresolv.h>
 #include <netdb.h>
+#include <sys/time.h>
 
 #include "connman.h"
 
@@ -40,6 +41,7 @@ static GSList *ts_list = NULL;
 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;
@@ -53,10 +55,26 @@ static void resolv_debug(const char *str, void *data)
 
 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, &timestamp);
 }
 
 static void save_timeservers(char **servers)
@@ -272,6 +290,7 @@ GSList *__connman_timeserver_get_all(struct connman_service *service)
 
 static gboolean ts_recheck(gpointer user_data)
 {
+       struct connman_service *service;
        GSList *ts;
 
        ts = __connman_timeserver_get_all(connman_service_get_default());
@@ -287,7 +306,8 @@ static gboolean ts_recheck(gpointer user_data)
 
                g_slist_free_full(ts, g_free);
 
-               __connman_timeserver_sync(NULL);
+               service = connman_service_get_default();
+               __connman_timeserver_sync(service);
 
                return FALSE;
        }
@@ -327,29 +347,16 @@ static void ts_recheck_enable(void)
                        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.
@@ -365,13 +372,12 @@ int __connman_timeserver_sync(struct connman_service *default_service)
        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);
 
@@ -381,15 +387,49 @@ int __connman_timeserver_sync(struct connman_service *default_service)
 
        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)
@@ -431,7 +471,9 @@ 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)
@@ -458,9 +500,13 @@ 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;
 }
index 4115758..c63dc81 100644 (file)
@@ -96,6 +96,8 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data);
 
 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("");
@@ -450,10 +452,14 @@ static void portal_manage_status(GWebResult *result,
                                &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,
@@ -751,7 +757,12 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
                                        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)) {
@@ -773,12 +784,8 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
                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:
@@ -991,6 +998,7 @@ int __connman_wispr_start(struct connman_service *service,
 
 void __connman_wispr_stop(struct connman_service *service)
 {
+       struct connman_wispr_portal *wispr_portal;
        int index;
 
        DBG("service %p", service);
@@ -1002,7 +1010,22 @@ void __connman_wispr_stop(struct connman_service *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)
@@ -1013,6 +1036,9 @@ 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;
 }
 
index 8403f91..c6edd76 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/python
 
-import gobject
+from gi.repository import GLib
 
 import dbus
 import dbus.mainloop.glib
@@ -82,6 +82,6 @@ if __name__ == '__main__':
        bus.add_match_string("member=Update,interface=net.connman.Notification")
        bus.add_message_filter(message_filter)
 
-       mainloop = gobject.MainLoop()
+       mainloop = GLib.MainLoop()
 
        mainloop.run()
index d570e5f..c520a8c 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/python
 
-import gobject
+from gi.repository import GLib
 
 import dbus
 import dbus.mainloop.glib
@@ -102,5 +102,5 @@ if __name__ == '__main__':
                                signal_name="PropertyChanged",
                                path_keyword="path")
 
-       mainloop = gobject.MainLoop()
+       mainloop = GLib.MainLoop()
        mainloop.run()
index 339d5eb..22501fc 100755 (executable)
@@ -3,10 +3,9 @@
 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'
@@ -32,7 +31,7 @@ class InputLine:
         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()
 
@@ -42,7 +41,7 @@ class InputLine:
         stdout.flush()
 
     def input_cb(self, fd, event):
-        if event != glib.IO_IN:
+        if event != GLib.IO_IN:
             return
 
         self.line += fd.read();
@@ -610,10 +609,6 @@ def build_args(parser):
     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)
@@ -634,7 +629,7 @@ def main():
 
     bus = dbus.SystemBus()
 
-    mainloop = gobject.MainLoop()
+    mainloop = GLib.MainLoop()
 
     wpa_s = Wpa_s(bus, args.ifname, args.command + params)
 
index 282785e..04de3f6 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/python
 
-import gobject
+from gi.repository import GLib
 
 import dbus
 import dbus.service
@@ -351,7 +351,7 @@ if __name__ == '__main__':
                except:
                        "Cannot register vpn agent"
 
-       mainloop = gobject.MainLoop()
+       mainloop = GLib.MainLoop()
        mainloop.run()
 
        #manager.UnregisterAgent(path)
index c09aabc..4c55162 100755 (executable)
@@ -1,7 +1,8 @@
 #!/usr/bin/python
 
+from gi.repository import GLib
+
 import sys
-import gobject
 
 import dbus
 import dbus.service
@@ -73,7 +74,7 @@ if __name__ == '__main__':
 
        manager.RegisterCounter(path, dbus.UInt32(10), dbus.UInt32(period))
 
-       mainloop = gobject.MainLoop()
+       mainloop = GLib.MainLoop()
        mainloop.run()
 
        #manager.UnregisterCounter(path)
index e45d22b..112074f 100755 (executable)
@@ -1,15 +1,13 @@
 #!/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):
@@ -293,11 +291,11 @@ def main():
        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)
 
index dd15bab..3bb52e1 100644 (file)
@@ -6,7 +6,7 @@ Type=dbus
 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
 
index 133acd2..92c63e2 100644 (file)
@@ -129,7 +129,6 @@ static gchar *option_plugin = NULL;
 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)
@@ -156,8 +155,6 @@ static GOptionEntry options[] = {
        { "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 },
@@ -259,7 +256,7 @@ int main(int argc, char *argv[])
 
        __connman_inotify_init();
        __connman_agent_init();
-       __vpn_provider_init(option_routes);
+       __vpn_provider_init();
        __vpn_manager_init();
        __vpn_ipconfig_init();
        __vpn_rtnl_init();
index 48894aa..1e4fcd1 100644 (file)
@@ -246,6 +246,8 @@ static int l2tp_notify(DBusMessage *msg, struct vpn_provider *provider)
                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);
 
index d600e61..fc6ceff 100644 (file)
@@ -42,6 +42,8 @@
 #include <connman/setting.h>
 #include <connman/vpn-dbus.h>
 
+#include <openconnect.h>
+
 #include "../vpn-provider.h"
 #include "../vpn-agent.h"
 
@@ -89,7 +91,6 @@ enum oc_connect_type {
 
 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;
@@ -98,21 +99,46 @@ struct oc_private_data {
        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)
@@ -139,11 +165,7 @@ static void close_io_channel(struct oc_private_data *data, GIOChannel *channel)
        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;
@@ -167,6 +189,9 @@ static void free_private_data(struct oc_private_data *data)
 
        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);
 
@@ -175,7 +200,6 @@ static void free_private_data(struct oc_private_data *data)
        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);
@@ -357,6 +381,8 @@ static int oc_notify(DBusMessage *msg, struct vpn_provider *provider)
        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);
 
@@ -371,7 +397,7 @@ static int oc_notify(DBusMessage *msg, struct vpn_provider *provider)
        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;
 
@@ -426,37 +452,6 @@ static void oc_died(struct connman_task *task, int exit_code, void *user_data)
        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;
@@ -472,56 +467,170 @@ static bool strv_contains_prefix(const char *strv[], const char *str)
        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);
@@ -551,39 +660,44 @@ static void request_input_pkcs_reply(DBusMessage *reply, void *user_data)
                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[] = {
@@ -591,21 +705,8 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
                                "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;
@@ -618,51 +719,12 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
 
        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);
 
@@ -687,44 +749,12 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
 
                                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;
@@ -736,13 +766,14 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
                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:
                        /*
@@ -756,168 +787,278 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
                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");
 
@@ -925,66 +1066,30 @@ static int run_connect(struct oc_private_data *data)
 
        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;
@@ -992,22 +1097,6 @@ static int run_connect(struct oc_private_data *data)
 
        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 */
@@ -1022,7 +1111,7 @@ static int run_connect(struct oc_private_data *data)
        }
 
 done:
-       clear_provider_credentials(data->provider);
+       clear_provider_credentials(data->provider, err != -EINPROGRESS);
 
        return err;
 }
@@ -1119,8 +1208,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
 
        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);
@@ -1131,8 +1222,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *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);
@@ -1224,41 +1317,53 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
 
        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;
@@ -1299,7 +1404,7 @@ static int request_input_credentials(struct oc_private_data *data,
 
        /*
         * 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.
@@ -1343,6 +1448,16 @@ static int request_input_credentials(struct oc_private_data *data,
                                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");
@@ -1354,7 +1469,7 @@ static int request_input_credentials(struct oc_private_data *data,
        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);
 
@@ -1366,6 +1481,12 @@ static int request_input_credentials(struct oc_private_data *data,
        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)
 {
@@ -1395,7 +1516,7 @@ static int oc_connect(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;
@@ -1481,7 +1602,9 @@ static int oc_connect(struct vpn_provider *provider,
                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);
@@ -1497,6 +1620,8 @@ request_input:
 
 static void oc_disconnect(struct vpn_provider *provider)
 {
+       struct oc_private_data *data;
+
        connman_info("provider %p", provider);
 
        if (!provider)
@@ -1508,6 +1633,19 @@ static void oc_disconnect(struct vpn_provider *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)
@@ -1547,7 +1685,7 @@ static int oc_error_code(struct vpn_provider *provider, int exit_code)
        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 */
@@ -1557,7 +1695,8 @@ static int oc_error_code(struct vpn_provider *provider, int exit_code)
 }
 
 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;
index bc0303c..daf66cd 100644 (file)
@@ -51,7 +51,6 @@
 #include "../vpn-agent.h"
 
 #include "vpn.h"
-#include "../vpn.h"
 
 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
 
@@ -84,6 +83,9 @@ struct {
        { "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 {
@@ -290,6 +292,7 @@ static int ov_notify(DBusMessage *msg, struct vpn_provider *provider)
 
        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) {
@@ -497,16 +500,13 @@ static int run_connect(struct ov_private_data *data,
        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
@@ -516,8 +516,24 @@ static int run_connect(struct ov_private_data *data,
         * 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) {
index 5fc861e..4a704bb 100644 (file)
 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 },
@@ -204,6 +207,7 @@ static int pptp_notify(DBusMessage *msg, struct vpn_provider *provider)
                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);
 
@@ -436,7 +440,9 @@ static int run_connect(struct vpn_provider *provider,
                        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;
 
@@ -450,16 +456,11 @@ static int run_connect(struct vpn_provider *provider,
        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);
@@ -474,7 +475,7 @@ static int run_connect(struct vpn_provider *provider,
                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;
@@ -485,8 +486,15 @@ static int run_connect(struct vpn_provider *provider,
                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");
 
index e04670c..cb0d304 100644 (file)
@@ -65,7 +65,7 @@ struct vpn_data {
 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;
 };
 
@@ -421,61 +421,26 @@ exist_err:
        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)
@@ -508,8 +473,8 @@ 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;
@@ -632,7 +597,7 @@ static int vpn_connect(struct vpn_provider *provider,
                        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;
@@ -812,7 +777,7 @@ static int vpn_route_env_parse(struct vpn_provider *provider, const char *key,
        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;
index 71e04f6..fd10add 100644 (file)
@@ -58,7 +58,7 @@ struct vpn_driver {
                        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);
index 8350fc3..d11b911 100644 (file)
 #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>
 
@@ -50,6 +54,7 @@
 #include "../vpn.h"
 
 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+#define PID_PATH_ROOT "/var/run/user"
 
 enum {
        OPT_STRING = 1,
@@ -240,6 +245,7 @@ static int vc_notify(DBusMessage *msg, struct vpn_provider *provider)
        }
 
        connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway);
+       connman_ipaddress_set_p2p(ipaddress, true);
        vpn_provider_set_ipaddress(provider, ipaddress);
 
        g_free(address);
@@ -430,14 +436,49 @@ static gboolean io_channel_cb(GIOChannel *source, GIOCondition condition,
        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;
@@ -473,6 +514,20 @@ static int run_connect(struct vc_private_data *data)
                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");
@@ -619,8 +674,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
 
        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);
@@ -631,8 +688,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *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);
@@ -687,17 +746,22 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
                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,
index de2dbda..0365894 100644 (file)
 #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;
@@ -116,7 +134,7 @@ static int parse_allowed_ips(const char *allowed_ips, wg_peer *peer)
        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;
@@ -151,7 +169,7 @@ static int parse_endpoint(const char *host, const char *port, wg_peer *peer)
                return -EINVAL;
        }
 
-       memcpy(&peer->endpoint.addr, rp->ai_addr, rp->ai_addrlen);
+       memcpy(addr, rp->ai_addr, rp->ai_addrlen);
        freeaddrinfo(result);
 
        return 0;
@@ -194,6 +212,8 @@ static int parse_address(const char *address, const char *gateway,
                err = -EINVAL;
        }
 
+       connman_ipaddress_set_p2p(*ipaddress, true);
+
        g_strfreev(tokens);
        if (err)
                connman_ipaddress_free(*ipaddress);
@@ -225,7 +245,7 @@ static char *get_ifname(void)
        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;
@@ -236,10 +256,53 @@ static char *get_ifname(void)
        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,
@@ -323,10 +386,14 @@ static int wg_connect(struct vpn_provider *provider,
                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");
@@ -342,7 +409,7 @@ static int wg_connect(struct vpn_provider *provider,
                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);
@@ -367,6 +434,11 @@ done:
 
        connman_ipaddress_free(ipaddress);
 
+       if (!err)
+               info->reresolve_id =
+                       g_timeout_add_seconds(DNS_RERESOLVE_TIMEOUT,
+                                               wg_dns_reresolve_cb, info);
+
        return err;
 }
 
@@ -377,10 +449,16 @@ static void wg_disconnect(struct vpn_provider *provider)
        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);
 }
 
index f56e51e..8c6b068 100644 (file)
@@ -229,7 +229,7 @@ static int load_provider(GKeyFile *keyfile, const char *group,
 
        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;
@@ -252,7 +252,7 @@ static int load_provider(GKeyFile *keyfile, const char *group,
 
                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;
        }
@@ -580,3 +580,18 @@ char **__vpn_config_get_string_list(GKeyFile *key_file,
 
        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;
+}
index c096fa3..825b43c 100644 (file)
@@ -108,7 +108,7 @@ unsigned int __vpn_ipconfig_get_flags_from_index(int index)
        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;
@@ -211,7 +211,7 @@ int __vpn_ipconfig_address_add(struct vpn_ipconfig *ipconfig, int family)
 
        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);
@@ -282,7 +282,10 @@ static struct vpn_ipconfig *create_ipv6config(int index)
                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);
 
@@ -314,7 +317,10 @@ struct vpn_ipconfig *__vpn_ipconfig_create(int index, int family)
                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);
 
index 5ce9328..59c805c 100644 (file)
@@ -44,7 +44,6 @@ static DBusConnection *connection;
 static GHashTable *provider_hash;
 static GSList *driver_list;
 static int configuration_count;
-static bool handle_routes;
 
 struct vpn_route {
        int family;
@@ -71,6 +70,7 @@ struct vpn_provider {
        char *host;
        char *domain;
        int family;
+       bool do_split_routing;
        GHashTable *routes;
        struct vpn_provider_driver *driver;
        void *driver_data;
@@ -91,6 +91,7 @@ struct vpn_provider {
        void *plugin_data;
        unsigned int auth_error_counter;
        unsigned int conn_error_counter;
+       unsigned int signal_watch;
 };
 
 static void append_properties(DBusMessageIter *iter,
@@ -366,22 +367,8 @@ static void set_user_networks(struct vpn_provider *provider, GSList *networks)
 static void del_routes(struct vpn_provider *provider)
 {
        GHashTableIter hash;
-       gpointer value, key;
 
        g_hash_table_iter_init(&hash, provider->user_routes);
-       while (handle_routes && g_hash_table_iter_next(&hash,
-                                               &key, &value)) {
-               struct vpn_route *route = value;
-               if (route->family == AF_INET6) {
-                       unsigned char prefixlen = atoi(route->netmask);
-                       connman_inet_del_ipv6_network_route(provider->index,
-                                                       route->network,
-                                                       prefixlen);
-               } else
-                       connman_inet_del_host_route(provider->index,
-                                               route->network);
-       }
-
        g_hash_table_remove_all(provider->user_routes);
        g_slist_free_full(provider->user_networks, free_route);
        provider->user_networks = NULL;
@@ -404,6 +391,16 @@ static void send_value(const char *path, const char *key, const char *value)
                                        &str);
 }
 
+static void send_value_boolean(const char *path, const char *key,
+                                                       dbus_bool_t value)
+{
+       connman_dbus_property_changed_basic(path,
+                                       VPN_CONNECTION_INTERFACE,
+                                       key,
+                                       DBUS_TYPE_BOOLEAN,
+                                       &value);
+}
+
 static gboolean provider_send_changed(gpointer data)
 {
        struct vpn_provider *provider = data;
@@ -474,6 +471,11 @@ static bool compare_network_lists(GSList *a, GSList *b)
        return true;
 }
 
+static const char *bool2str(bool value)
+{
+       return value ? "true" : "false";
+}
+
 static int set_provider_property(struct vpn_provider *provider,
                        const char *name, DBusMessageIter *value, int type)
 {
@@ -500,10 +502,17 @@ static int set_provider_property(struct vpn_provider *provider,
                del_routes(provider);
                provider->user_networks = networks;
                set_user_networks(provider, provider->user_networks);
+               send_routes(provider, provider->user_routes, "UserRoutes");
+       } else if (g_str_equal(name, "SplitRouting")) {
+               dbus_bool_t split_routing;
+
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return -EINVAL;
+
+               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;
 
@@ -576,8 +585,13 @@ static DBusMessage *set_properties(DBusMessageIter *iter, DBusMessage *msg,
                dbus_message_iter_recurse(&entry, &value);
 
                type = dbus_message_iter_get_arg_type(&value);
-               /* Ignore and report back all invalid property types */
-               if (type != DBUS_TYPE_STRING && type != DBUS_TYPE_ARRAY) {
+               switch (type) {
+               case DBUS_TYPE_STRING:
+               case DBUS_TYPE_ARRAY:
+               case DBUS_TYPE_BOOLEAN:
+                       break;
+               default:
+                       /* Ignore and report back all invalid property types */
                        invalid = append_to_gstring(invalid, key);
                        continue;
                }
@@ -702,8 +716,7 @@ static DBusMessage *clear_property(DBusConnection *conn, DBusMessage *msg,
 
                del_routes(provider);
 
-               if (!handle_routes)
-                       send_routes(provider, provider->user_routes, name);
+               send_routes(provider, provider->user_routes, name);
        } else if (vpn_provider_get_string(provider, name)) {
                err = vpn_provider_set_string(provider, name, NULL);
                switch (err) {
@@ -841,6 +854,8 @@ static void provider_resolv_host_addr(struct vpn_provider *provider)
 void __vpn_provider_append_properties(struct vpn_provider *provider,
                                                        DBusMessageIter *iter)
 {
+       dbus_bool_t split_routing;
+
        if (provider->host)
                connman_dbus_dict_append_basic(iter, "Host",
                                        DBUS_TYPE_STRING, &provider->host);
@@ -852,6 +867,10 @@ void __vpn_provider_append_properties(struct vpn_provider *provider,
        if (provider->type)
                connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
                                                 &provider->type);
+
+       split_routing = provider->do_split_routing;
+       connman_dbus_dict_append_basic(iter, "SplitRouting", DBUS_TYPE_BOOLEAN,
+                                                       &split_routing);
 }
 
 int __vpn_provider_append_user_route(struct vpn_provider *provider,
@@ -984,7 +1003,7 @@ static GSList *get_routes(gchar **networks)
 static int provider_load_from_keyfile(struct vpn_provider *provider,
                GKeyFile *keyfile)
 {
-       gsize idx = 0;
+       gsize idx;
        gchar **settings;
        gchar *key, *value;
        gsize length, num_user_networks;
@@ -997,28 +1016,26 @@ static int provider_load_from_keyfile(struct vpn_provider *provider,
                return -ENOENT;
        }
 
-       while (idx < length) {
+       for (idx = 0; idx < length; idx++) {
                key = settings[idx];
-               if (key) {
-                       if (g_str_equal(key, "Networks")) {
-                               networks = __vpn_config_get_string_list(keyfile,
-                                               provider->identifier,
-                                               key,
-                                               &num_user_networks,
+               if (!key)
+                       continue;
+
+               if (g_str_equal(key, "Networks")) {
+                       networks = __vpn_config_get_string_list(keyfile,
+                                               provider->identifier,key,
+                                               &num_user_networks, NULL);
+                       provider->user_networks = get_routes(networks);
+               } else {
+                       value = __vpn_config_get_string(keyfile,
+                                               provider->identifier, key,
                                                NULL);
-                               provider->user_networks = get_routes(networks);
-
-                       } else {
-                               value = __vpn_config_get_string(keyfile,
-                                                       provider->identifier,
-                                                       key, NULL);
-                               vpn_provider_set_string(provider, key,
-                                                       value);
-                               g_free(value);
-                       }
+
+                       vpn_provider_set_string(provider, key, value);
+                       g_free(value);
                }
-               idx += 1;
        }
+
        g_strfreev(settings);
        g_strfreev(networks);
 
@@ -1135,6 +1152,7 @@ static int vpn_provider_save(struct vpn_provider *provider)
                        "Host", provider->host);
        g_key_file_set_string(keyfile, provider->identifier,
                        "VPN.Domain", provider->domain);
+
        if (provider->user_networks) {
                gchar **networks;
                gsize network_count;
@@ -1725,6 +1743,7 @@ static void append_properties(DBusMessageIter *iter,
        GHashTableIter hash;
        gpointer value, key;
        dbus_bool_t immutable;
+       dbus_bool_t split_routing;
 
        connman_dbus_dict_open(iter, &dict);
 
@@ -1752,6 +1771,10 @@ static void append_properties(DBusMessageIter *iter,
        connman_dbus_dict_append_basic(&dict, "Immutable", DBUS_TYPE_BOOLEAN,
                                        &immutable);
 
+       split_routing = provider->do_split_routing;
+       connman_dbus_dict_append_basic(&dict, "SplitRouting",
+                                       DBUS_TYPE_BOOLEAN, &split_routing);
+
        if (provider->family == AF_INET)
                connman_dbus_dict_append_dict(&dict, "IPv4", append_ipv4,
                                                provider);
@@ -1776,8 +1799,7 @@ static void append_properties(DBusMessageIter *iter,
                while (g_hash_table_iter_next(&hash, &key, &value)) {
                        struct vpn_setting *setting = value;
 
-                       if (!setting->hide_value &&
-                                                       setting->value)
+                       if (!setting->hide_value && setting->value)
                                connman_dbus_dict_append_basic(&dict, key,
                                                        DBUS_TYPE_STRING,
                                                        &setting->value);
@@ -1806,55 +1828,6 @@ static void connection_added_signal(struct vpn_provider *provider)
        dbus_message_unref(signal);
 }
 
-static bool check_host(char **hosts, char *host)
-{
-       int i;
-
-       if (!hosts)
-               return false;
-
-       for (i = 0; hosts[i]; i++) {
-               if (g_strcmp0(hosts[i], host) == 0)
-                       return true;
-       }
-
-       return false;
-}
-
-static void provider_append_routes(gpointer key, gpointer value,
-                                       gpointer user_data)
-{
-       struct vpn_route *route = value;
-       struct vpn_provider *provider = user_data;
-       int index = provider->index;
-
-       if (!handle_routes)
-               return;
-
-       /*
-        * If the VPN administrator/user has given a route to
-        * VPN server, then we must discard that because the
-        * server cannot be contacted via VPN tunnel.
-        */
-       if (check_host(provider->host_ip, route->network)) {
-               DBG("Discarding VPN route to %s via %s at index %d",
-                       route->network, route->gateway, index);
-               return;
-       }
-
-       if (route->family == AF_INET6) {
-               unsigned char prefix_len = atoi(route->netmask);
-
-               connman_inet_add_ipv6_network_route(index, route->network,
-                                                       route->gateway,
-                                                       prefix_len);
-       } else {
-               connman_inet_add_network_route(index, route->network,
-                                               route->gateway,
-                                               route->netmask);
-       }
-}
-
 static int set_connected(struct vpn_provider *provider,
                                        bool connected)
 {
@@ -1871,18 +1844,8 @@ static int set_connected(struct vpn_provider *provider,
 
                __vpn_ipconfig_address_add(ipconfig, provider->family);
 
-               if (handle_routes)
-                       __vpn_ipconfig_gateway_add(ipconfig, provider->family);
-
                provider_indicate_state(provider,
                                        VPN_PROVIDER_STATE_READY);
-
-               g_hash_table_foreach(provider->routes, provider_append_routes,
-                                       provider);
-
-               g_hash_table_foreach(provider->user_routes,
-                                       provider_append_routes, provider);
-
        } else {
                provider_indicate_state(provider,
                                        VPN_PROVIDER_STATE_DISCONNECT);
@@ -1949,10 +1912,61 @@ int vpn_provider_indicate_error(struct vpn_provider *provider,
        return 0;
 }
 
+static gboolean provider_property_changed(DBusConnection *conn,
+                                       DBusMessage *message, void *user_data)
+{
+       DBusMessageIter iter;
+       DBusMessageIter value;
+       struct vpn_provider *provider = user_data;
+       const char *key;
+
+       if (!dbus_message_iter_init(message, &iter))
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       DBG("provider %p key %s", provider, key);
+
+       if (g_str_equal(key, "SplitRouting")) {
+               dbus_bool_t split_routing;
+
+               if (dbus_message_iter_get_arg_type(&value) !=
+                                                       DBUS_TYPE_BOOLEAN)
+                       goto out;
+
+               dbus_message_iter_get_basic(&value, &split_routing);
+
+               DBG("property %s value %s", key, bool2str(split_routing));
+
+               /*
+                * Even though this is coming from connmand, signal the value
+                * for other components listening to the changes via VPN API
+                * only. provider.c will skip setting the same value in order
+                * to avoid signaling loop. This is needed for ensuring that
+                * all components using VPN API will be informed about the
+                * correct status of SplitRouting. Especially when loading the
+                * services after a crash, for instance.
+                */
+               vpn_provider_set_boolean(provider, "SplitRouting",
+                                       split_routing, true);
+       }
+
+out:
+       return TRUE;
+}
+
 static int connection_unregister(struct vpn_provider *provider)
 {
        DBG("provider %p path %s", provider, provider->path);
 
+       if (provider->signal_watch) {
+               g_dbus_remove_watch(connection, provider->signal_watch);
+               provider->signal_watch = 0;
+       }
+
        if (!provider->path)
                return -EALREADY;
 
@@ -1967,6 +1981,8 @@ static int connection_unregister(struct vpn_provider *provider)
 
 static int connection_register(struct vpn_provider *provider)
 {
+       char *connmand_vpn_path;
+
        DBG("provider %p path %s", provider, provider->path);
 
        if (provider->path)
@@ -1980,6 +1996,18 @@ static int connection_register(struct vpn_provider *provider)
                                connection_methods, connection_signals,
                                NULL, provider, NULL);
 
+       connmand_vpn_path = g_strdup_printf("%s/service/vpn_%s", CONNMAN_PATH,
+                                               provider->identifier);
+
+       provider->signal_watch = g_dbus_add_signal_watch(connection,
+                                       CONNMAN_SERVICE, connmand_vpn_path,
+                                       CONNMAN_SERVICE_INTERFACE,
+                                       PROPERTY_CHANGED,
+                                       provider_property_changed,
+                                       provider, NULL);
+
+       g_free(connmand_vpn_path);
+
        return 0;
 }
 
@@ -2017,6 +2045,7 @@ static void provider_initialize(struct vpn_provider *provider)
        provider->domain = NULL;
        provider->identifier = NULL;
        provider->immutable = false;
+       provider->do_split_routing = false;
        provider->user_networks = NULL;
        provider->routes = g_hash_table_new_full(g_direct_hash, g_direct_equal,
                                        NULL, free_route);
@@ -2112,9 +2141,12 @@ static struct vpn_provider *provider_create_from_keyfile(GKeyFile *keyfile,
                        return NULL;
                }
 
-               if (provider_register(provider) == 0)
+               if (!provider_register(provider)) {
                        connection_register(provider);
+                       connection_added_signal(provider);
+               }
        }
+
        return provider;
 }
 
@@ -2166,9 +2198,10 @@ char *__vpn_provider_create_identifier(const char *host, const char *domain)
 {
        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);
 
@@ -2184,6 +2217,7 @@ int __vpn_provider_create(DBusMessage *msg)
        GSList *networks = NULL;
        char *ident;
        int err;
+       dbus_bool_t split_routing = false;
 
        dbus_message_iter_init(msg, &iter);
        dbus_message_iter_recurse(&iter, &array);
@@ -2210,6 +2244,11 @@ int __vpn_provider_create(DBusMessage *msg)
                                        g_str_equal(key, "Domain"))
                                dbus_message_iter_get_basic(&value, &domain);
                        break;
+               case DBUS_TYPE_BOOLEAN:
+                       if (g_str_equal(key, "SplitRouting"))
+                               dbus_message_iter_get_basic(&value,
+                                                       &split_routing);
+                       break;
                case DBUS_TYPE_ARRAY:
                        if (g_str_equal(key, "UserRoutes"))
                                networks = get_user_networks(&value);
@@ -2219,7 +2258,7 @@ int __vpn_provider_create(DBusMessage *msg)
                dbus_message_iter_next(&array);
        }
 
-       if (!host || !domain)
+       if (!host)
                return -EINVAL;
 
        DBG("Type %s name %s networks %p", type, name, networks);
@@ -2243,6 +2282,7 @@ int __vpn_provider_create(DBusMessage *msg)
                provider->domain = g_strdup(domain);
                provider->name = g_strdup(name);
                provider->type = g_strdup(type);
+               provider->do_split_routing = split_routing;
 
                if (provider_register(provider) == 0)
                        vpn_provider_load(provider);
@@ -2404,7 +2444,7 @@ int __vpn_provider_create_from_config(GHashTable *settings,
        networks_str = get_string(settings, "Networks");
        networks = parse_user_networks(networks_str);
 
-       if (!host || !domain) {
+       if (!host) {
                err = -EINVAL;
                goto fail;
        }
@@ -2563,6 +2603,10 @@ static int set_string(struct vpn_provider *provider,
                g_free(provider->domain);
                provider->domain = g_strdup(value);
                send_value(provider->path, "Domain", provider->domain);
+       } else if (g_str_equal(key, "SplitRouting")) {
+               connman_warn("VPN SplitRouting value attempted to set as "
+                                       "string, is boolean");
+               return -EINVAL;
        } else {
                struct vpn_setting *setting;
                bool replace = true;
@@ -2649,6 +2693,25 @@ const char *vpn_provider_get_string(struct vpn_provider *provider,
        return setting->value;
 }
 
+int vpn_provider_set_boolean(struct vpn_provider *provider, const char *key,
+                                               bool value, bool force_change)
+{
+       DBG("provider %p key %s", provider, key);
+
+       if (g_str_equal(key, "SplitRouting")) {
+               if (provider->do_split_routing == value && !force_change)
+                       return -EALREADY;
+
+               DBG("SplitRouting set to %s", bool2str(value));
+
+               provider->do_split_routing = value;
+               send_value_boolean(provider->path, key,
+                                       provider->do_split_routing);
+       }
+
+       return 0;
+}
+
 bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key,
                                                        bool default_value)
 {
@@ -2917,11 +2980,8 @@ int vpn_provider_append_route(struct vpn_provider *provider,
                break;
        }
 
-       if (!handle_routes) {
-               if (route->netmask && route->gateway &&
-                                                       route->network)
-                       provider_schedule_changed(provider);
-       }
+       if (route->netmask && route->gateway && route->network)
+               provider_schedule_changed(provider);
 
        return 0;
 }
@@ -2980,6 +3040,12 @@ void vpn_provider_driver_unregister(struct vpn_provider_driver *driver)
                if (provider && provider->driver &&
                                g_strcmp0(provider->driver->name,
                                                        driver->name) == 0) {
+                       /*
+                        * Cancel VPN agent request to avoid segfault at
+                        * shutdown as the callback, if set can point to a
+                        * function in the plugin that is to be removed.
+                        */
+                       connman_agent_cancel(provider);
                        provider->driver = NULL;
                }
        }
@@ -3058,7 +3124,7 @@ void vpn_provider_clear_address(struct vpn_provider *provider, int family)
                        DBG("ipv6 %s/%d", address, len);
 
                        connman_inet_clear_ipv6_address(provider->index,
-                                                       address, len);
+                                               provider->prev_ipv6_addr);
 
                        connman_ipaddress_free(provider->prev_ipv6_addr);
                        provider->prev_ipv6_addr = NULL;
@@ -3148,14 +3214,12 @@ static void remove_unprovisioned_providers(void)
        g_strfreev(providers);
 }
 
-int __vpn_provider_init(bool do_routes)
+int __vpn_provider_init(void)
 {
        int err;
 
        DBG("");
 
-       handle_routes = do_routes;
-
        err = connman_agent_driver_register(&agent_driver);
        if (err < 0) {
                connman_error("Cannot register agent driver for %s",
index 0275d51..f7fa859 100644 (file)
@@ -83,6 +83,9 @@ const char *vpn_provider_get_string(struct vpn_provider *provider,
                                                        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);
 
@@ -118,6 +121,7 @@ const char *vpn_provider_get_save_group(struct vpn_provider *provider);
 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);
index 6ddfd83..295c05c 100644 (file)
@@ -184,7 +184,7 @@ int vpn_rtnl_register(struct vpn_rtnl *rtnl)
        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;
 }
index 0eca2bc..e78e501 100644 (file)
@@ -2,7 +2,7 @@
  *  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
@@ -23,6 +23,9 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <sys/types.h>
 
 #include <connman/log.h>
 
@@ -37,11 +40,13 @@ static struct {
        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 {
@@ -52,6 +57,58 @@ 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)
@@ -129,6 +186,9 @@ static void parse_config(GKeyFile *config, const char *file)
        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)
@@ -245,6 +305,7 @@ void __vpn_settings_cleanup()
        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);
diff --git a/vpn/vpn-util.c b/vpn/vpn-util.c
new file mode 100644 (file)
index 0000000..9ef14d3
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ *  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;
+}
+
index 45cf46d..477cb22 100644 (file)
--- a/vpn/vpn.h
+++ b/vpn/vpn.h
@@ -35,7 +35,7 @@ struct vpn_ipconfig;
 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);
@@ -85,7 +85,6 @@ int __vpn_provider_create_from_config(GHashTable *settings,
 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);
@@ -97,7 +96,7 @@ int __vpn_provider_disconnect(struct vpn_provider *provider);
 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"
 
@@ -112,10 +111,12 @@ int __vpn_rtnl_send(const void *buf, size_t len);
 
 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);
@@ -132,3 +133,8 @@ const char * vpn_settings_get_binary_user(struct vpn_plugin_data *data);
 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);