Imported Upstream version 1.41 86/289886/1 upstream/1.41
authorAnjali Nijhara <a.nijhara@samsung.com>
Wed, 15 Mar 2023 06:29:53 +0000 (11:59 +0530)
committerAnjali Nijhara <a.nijhara@samsung.com>
Wed, 15 Mar 2023 06:31:07 +0000 (12:01 +0530)
Change-Id: Ida2a62a81c771f833353d1a9a77604e6f2b06b88

46 files changed:
AUTHORS
ChangeLog
README
acinclude.m4
client/agent.c
client/commands.c
configure.ac
doc/connman.conf.5.in
doc/technology-api.txt
doc/vpn-agent-api.txt
doc/vpn-connection-api.txt
gdhcp/client.c
gsupplicant/gsupplicant.h
gsupplicant/supplicant.c
include/technology.h
plugins/bluetooth.c
plugins/ethernet.c
plugins/gadget.c
plugins/iwd.c
plugins/neard.c
plugins/vpn.c
plugins/wifi.c
src/agent.c
src/config.c
src/dbus.c
src/dnsproxy.c
src/inet.c
src/ipconfig.c
src/main.c
src/main.conf
src/manager.c
src/network.c
src/rtnl.c
src/service.c
src/technology.c
src/wispr.c
tools/ip6tables-test.c
tools/iptables-test.c
unit/test-iptables.c
vpn/plugins/l2tp.c
vpn/plugins/openconnect.c
vpn/plugins/openvpn.c
vpn/plugins/pptp.c
vpn/vpn-provider.c
vpn/vpn-provider.h
vpn/vpn-rtnl.c

diff --git a/AUTHORS b/AUTHORS
index 438d45f3869bffade2917da6bcdee59eb0e30f9e..3ed098505c02f27d8eb31c2deff161432ef909f0 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -145,7 +145,7 @@ André Draszik <andre.draszik@jci.com>
 Chris Novakovic <chris@chrisn.me.uk>
 Ryan Schaefer <ryan.schaefer@flukenetworks.com>
 Nicolas Cornu <n.cornu@overkiz.com>
-Rahul Jain <rahul.jain@samsung.com
+Rahul Jain <rahul.jain@samsung.com>
 Benoît Monin <benoit.monin@gmx.fr>
 Jussi Laakkonen <jussi.laakkonen@jolla.com>
 Vivien Henriet <v.henriet@overkiz.com>
@@ -171,3 +171,8 @@ Gabriel FORTE <gforte@wyplay.com>
 Colin Wee <cwee@tesla.com>
 Valery Kashcheev <v.kascheev@omp.ru>
 Alyssa Ross <hi@alyssa.is>
+Ariel D'Alessandro <ariel.dalessandro@collabora.com>
+Lukáš Karas <lukas.karas@centrum.cz>
+Michael Nazzareno Trimarchi <michael@amarulasolutions.com>
+Christian Taedcke <christian.taedcke@lemonbeat.com>
+Matthias Gerstner <mgerstner@suse.de>
index 69207d2a5659fe55d1e7253443eb4bb6b51f497d..11566a53aaf94de342df26a5d4d0940058630a98 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+ver 1.41:
+       Fix issue with RTNL netlink message alignment.
+       Fix issue with dnsproxy and timeout for TCP feature.
+       Fix issue with dnsproxy and busy loop in TCP server.
+       Fix issue with WiFi connection with no passphrase.
+       Add support for wpa_supplicant and WPA3-SAE functionality.
+       Add support for D-Bus ObjectManager interface.
+
 ver 1.40:
        Fix issue with handling WiFi disconnecting status.
        Fix issue with handling WiFi auto-connect and iwd backend.
diff --git a/README b/README
index b8154e67a7accb7b452727dd7ad81977a15d8476..e3268c829f89ecc0f5dc6ec66a71b28c4a4cbb03 100644 (file)
--- a/README
+++ b/README
@@ -408,8 +408,17 @@ from ipv4.connman.net (for IPv4 connectivity) and ipv6.connman.net
 (for IPv6 connectivity). The used URL looks like this
 http://ipv{4|6}.connman.net/online/status.html
 
+When an online check request fails, another one is triggered after a
+longer interval. The intervals follow the square series of numbers
+in a specific range, by default [1, 12], corresponding to the following
+intervals, in seconds: 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121 and 144.
+
 See connman.conf(5) for the EnableOnlineCheck option, if you need to
 disable the feature.
+It is also possible to specify other URLs via OnlineCheckIPv4URL and
+OnlineCheckIPv6URL options.
+The range of intervals between two online check requests can be fine-tuned
+via OnlineCheckInitialInterval and OnlineCheckMaxInterval options.
 
 During the online check procedure, ConnMan will temporarily install
 a host route to both the ipv4.connman.net and ipv6.connman.net so that
@@ -453,3 +462,7 @@ send a (empty) message from your email account to
 
 Mailing list archive:
        https://lore.kernel.org/connman
+
+IRC:
+       ircs://irc.oftc.net:6697/#connman (for SSL)
+       irc://irc.oftc.net:6667/#connman (for non-SSL)
index 9e8e0dc5e8b2d6b11a1c0400dbda6c5d89b896c6..262465d8085df3624583bfad0f9dd88b4bf5746a 100644 (file)
@@ -21,7 +21,9 @@ AC_DEFUN([COMPILER_FLAGS], [
                CFLAGS+=" -Wdeclaration-after-statement"
                CFLAGS+=" -Wmissing-declarations"
                CFLAGS+=" -Wredundant-decls"
-               CFLAGS+=" -Wcast-align"
+                if ( $CC -v 2>/dev/null | grep "gcc version" ); then
+                        CFLAGS+=" -Wcast-align"
+                fi
                CFLAGS="$CFLAGS -DG_DISABLE_DEPRECATED"
        fi
 ])
index 1cad3e03626a23e9b2483bed52dda3e9309e5f86..94ace7cd4b822e19ac87ded0ecef4ff82b86328e 100644 (file)
@@ -100,6 +100,8 @@ static struct agent_input_data vpnagent_input_handler[] = {
          request_input_string_return },
        { "OpenConnect.VPNHost", false, "OpenConnect VPN server? ",
          request_input_string_return },
+       { "OpenConnect.SecondPassword", false, "VPN one-time password? ",
+         request_input_string_return },
        { "Username", false, "VPN username? ", request_input_string_return },
        { "Password", false, "VPN password? ", request_input_string_return },
        { },
index 94c375dd198d0f445d88ecff29d191da24efd567..53cc14c8fcbfcf61df66e84df9b361c4c9ec24cc 100644 (file)
@@ -550,20 +550,23 @@ struct tether_properties {
        int ssid_result;
        int passphrase_result;
        int set_tethering;
+       int freq_result;
 };
 
 static int tether_update(struct tether_properties *tether)
 {
        int ret;
 
-       if (tether->ssid_result == 0 && tether->passphrase_result == 0) {
+       if (tether->ssid_result == 0 && tether->passphrase_result == 0 &&
+                       tether->freq_result == 0) {
                ret = tether_set("wifi", tether->set_tethering);
                g_free(tether);
                return ret;
        }
 
        if (tether->ssid_result != -EINPROGRESS &&
-                       tether->passphrase_result != -EINPROGRESS) {
+                       tether->passphrase_result != -EINPROGRESS &&
+                       tether->freq_result != -EINPROGRESS) {
                g_free(tether);
                return 0;
        }
@@ -603,9 +606,25 @@ static int tether_set_passphrase_return(DBusMessageIter *iter, int errnum,
        return tether_update(tether);
 }
 
-static int tether_set_ssid(char *ssid, char *passphrase, int set_tethering)
+static int tether_set_freq_return(DBusMessageIter *iter, int errnum,
+                                 const char *error, void *user_data)
 {
-       struct tether_properties *tether = g_new(struct tether_properties, 1);
+       struct tether_properties *tether = user_data;
+
+       if (!error) {
+               fprintf(stdout, "Wifi access point frequency set\n");
+               tether->freq_result = 0;
+       } else {
+               fprintf(stderr, "Error setting wifi frequency: %s\n", error);
+               tether->freq_result = -EINVAL;
+       }
+
+       return tether_update(tether);
+}
+
+static int tether_set_ssid(char *ssid, char *passphrase, int set_tethering, int freq)
+{
+       struct tether_properties *tether = g_new0(struct tether_properties, 1);
 
        tether->set_tethering = set_tethering;
 
@@ -621,8 +640,17 @@ static int tether_set_ssid(char *ssid, char *passphrase, int set_tethering)
                        tether_set_passphrase_return, tether,
                        "TetheringPassphrase", DBUS_TYPE_STRING, &passphrase);
 
+       if (freq > 0) {
+               tether->freq_result =__connmanctl_dbus_set_property(connection,
+                               "/net/connman/technology/wifi",
+                               "net.connman.Technology",
+                               tether_set_freq_return, tether,
+                               "TetheringFreq", DBUS_TYPE_INT32, &freq);
+       }
+
        if (tether->ssid_result != -EINPROGRESS &&
-                       tether->passphrase_result != -EINPROGRESS) {
+                       tether->passphrase_result != -EINPROGRESS &&
+                       tether->freq_result != -EINPROGRESS) {
                g_free(tether);
                return -ENXIO;
        }
@@ -638,24 +666,30 @@ static int cmd_tether(char *args[], int num, struct connman_option *options)
        if (num < 3)
                return -EINVAL;
 
-       passphrase = args[num - 1];
-       ssid = args[num - 2];
-
        set_tethering = parse_boolean(args[2]);
 
        if (strcmp(args[1], "wifi") == 0) {
+               int freq = 0;
 
-               if (num > 5)
+               if (num > 6)
                        return -E2BIG;
 
-               if (num == 5 && set_tethering == -1)
+               if (num >= 5 && set_tethering == -1)
                        return -EINVAL;
 
                if (num == 4)
                        set_tethering = -1;
 
+               if (num == 6) {
+                       freq = atoi(args[num - 1]);
+                       num --;
+               }
+
+               passphrase = args[num - 1];
+               ssid = args[num - 2];
+
                if (num > 3)
-                       return tether_set_ssid(ssid, passphrase, set_tethering);
+                       return tether_set_ssid(ssid, passphrase, set_tethering, freq);
        }
 
        if (num > 3)
@@ -1425,7 +1459,6 @@ static void monitor_del(char *interface)
        int i;
        char *rule;
 
-
        for (i = 0; monitor[i].interface; i++) {
                if (g_strcmp0(interface, monitor[i].interface) == 0) {
                        if (monitor[i].enabled == false)
@@ -1811,7 +1844,6 @@ static int session_connect_cb(DBusMessageIter *iter, int errnum,
        return -EINPROGRESS;
 }
 
-
 static int session_connect(void)
 {
        return __connmanctl_dbus_method_call(connection, "net.connman",
@@ -2768,7 +2800,7 @@ static const struct {
          "Disables given technology or offline mode",
          lookup_technology_offline },
        { "tether", "<technology> on|off\n"
-                   "            wifi [on|off] <ssid> <passphrase> ",
+                   "            wifi [on|off] <ssid> <passphrase> [<freq>] ",
                                          NULL,            cmd_tether,
          "Enable, disable tethering, set SSID and passphrase for wifi",
          lookup_tether },
index b55d24877dc40969adb29857c569bf3b29c7856b..a573cefd712a3e811071d962489e1350dc4addc3 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(connman, 1.40)
+AC_INIT(connman, 1.41)
 
 AC_CONFIG_MACRO_DIR([m4])
 
index 2e06b3ef1f3c39e7625533b177b4d29dc6fdc793..82cceb7298f153f9da9e8686ee6df7988d9b4f9f 100644 (file)
@@ -167,13 +167,16 @@ transitioned to ONLINE state.
 If this setting is false, the default service will remain in READY state.
 Default value is true.
 .TP
+.BI OnlineCheckIPv4URL= url, OnlineCheckIPv6URL= url
+Urls (IPv4 and IPv6 respectively) used during the online status check.
+Please refer to the README for more detailed information.
+Default values are http://ipv4.connman.net/online/status.html and
+http://ipv6.connman.net/online/status.html respectively.
+.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.
+Please refer to the README for more detailed information.
+Default values are 1 and 12 respectively.
 .TP
 .BI EnableOnlineToReadyTransition=true\ \fR|\fB\ false
 WARNING: Experimental feature!!!
index f22e9b2907224269b47c305a17424018e95ee0e6..cdf3039693ceaaf7a24a9588ec4aa5da90f91b40 100644 (file)
@@ -100,3 +100,10 @@ Properties boolean Powered [readwrite]
                        This property is only valid for the WiFi technology,
                        and is then mapped to the WPA pre-shared key clients
                        will have to use in order to establish a connection.
+
+               int TetheringFreq [readwrite]
+
+                       The tethering access point frequency
+
+                       This property is only valid for the WiFi technology and it's
+                       optional. Default frequency is 2412 Mhz.
index ffa6fadd047e961afca93a27a11d08fc7cf5c47d..ecc8faaa2220abce46e3a5682a6459abcc9bc9c1 100644 (file)
@@ -85,6 +85,10 @@ Fields               string Username
                        Return the OpenConnect cookie value that is used for
                        authenticating the VPN session.
 
+               string OpenConnect.Group
+
+                       Choose authentication login group.
+
                string OpenConnect.PKCSClientCert
 
                        Informational field containing a PKCS#1/PKCS#8/PKCS#12
@@ -96,12 +100,21 @@ Fields             string Username
                        Password for decrypting PKCS#8/PKCS#12 client
                        certificate.
 
+               string OpenConnect.SecondPassword
+
+                       Second factor password for authentication.
+
                string OpenConnect.ServerCert
 
                        Return the OpenConnect server hash used to identify
                        the final server after possible web authentication
                        logins, selections and redirections.
 
+               boolean OpenConnect.UseSecondPassword
+
+                       Indicates that second factor password is used
+                       for selected authentication group.
+
                string OpenConnect.VPNHost
 
                        Return the final VPN server to use after possible
index 6e6293e4921e93726d3cef0c45f7de9c1d5917f8..2d3e0078b3b70232242ece685ffa06e9b8e72116 100644 (file)
@@ -235,6 +235,19 @@ Properties string State [readonly]
                        The VPN server activated route. These routes
                        are pushed to connman by VPN server.
 
+               string AuthErrorLimit
+
+                       This value defines the amount of authentication errors
+                       that are allowed before informing VPN agent to clear
+                       the credentials in case there was a previous successful
+                       VPN connection made within one hour. This is to be used
+                       with providers that allow only one login from one
+                       account at a time to prevent clearing of credentials
+                       when networks are rapidly changed. This value is used
+                       as an integer and if unset this default to "1" for all
+                       except OpenVPN that uses value "10". Setting value "0"
+                       disables the feature for the provider.
+
                There can be other properties also but as the VPN
                technologies are so different, they have different
                kind of options that they need, so not all options
index c7b85e58e2be5dffc951dd0fb0c12308e4f629e8..3016dfc2582b051b001db2e1ed91e7a74dacbebd 100644 (file)
@@ -2381,6 +2381,9 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                dhcp_client->retry_times = 0;
 
                option = dhcp_get_option(&packet, pkt_len, DHCP_SERVER_ID);
+               if (!option)
+                       return TRUE;
+
                dhcp_client->server_ip = get_be32(option);
                dhcp_client->requested_ip = ntohl(packet.yiaddr);
 
@@ -2442,6 +2445,8 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                        if (dhcp_client->state == REBOOTING) {
                                option = dhcp_get_option(&packet, pkt_len,
                                                        DHCP_SERVER_ID);
+                               if (!option)
+                                       return TRUE;
                                dhcp_client->server_ip = get_be32(option);
                        }
 
index 7935c3a6311899daeb57e0bf4c1fc5acbe242cdc..eab6293f0c5b7112ce5fa19a408c67753a2a97ab 100644 (file)
@@ -61,6 +61,7 @@ extern "C" {
 #define G_SUPPLICANT_KEYMGMT_WPA_EAP   (1 << 7)
 #define G_SUPPLICANT_KEYMGMT_WPA_EAP_256       (1 << 8)
 #define G_SUPPLICANT_KEYMGMT_WPS               (1 << 9)
+#define G_SUPPLICANT_KEYMGMT_SAE               (1 << 10)
 
 #define G_SUPPLICANT_PROTO_WPA         (1 << 0)
 #define G_SUPPLICANT_PROTO_RSN         (1 << 1)
@@ -129,6 +130,12 @@ typedef enum {
        G_SUPPLICANT_PEER_GROUP_FAILED,
 } GSupplicantPeerState;
 
+typedef enum {
+       G_SUPPLICANT_MFP_NONE,
+       G_SUPPLICANT_MFP_OPTIONAL,
+       G_SUPPLICANT_MFP_REQUIRED,
+} GSupplicantMfpOptions;
+
 struct _GSupplicantSSID {
        const void *ssid;
        unsigned int ssid_len;
@@ -155,6 +162,8 @@ struct _GSupplicantSSID {
        dbus_bool_t use_wps;
        const char *pin_wps;
        const char *bgscan;
+       unsigned int keymgmt;
+       GSupplicantMfpOptions ieee80211w;
 };
 
 typedef struct _GSupplicantSSID GSupplicantSSID;
@@ -339,6 +348,7 @@ bool g_supplicant_peer_is_in_a_group(GSupplicantPeer *peer);
 GSupplicantInterface *g_supplicant_peer_get_group_interface(GSupplicantPeer *peer);
 bool g_supplicant_peer_is_client(GSupplicantPeer *peer);
 bool g_supplicant_peer_has_requested_connection(GSupplicantPeer *peer);
+unsigned int g_supplicant_network_get_keymgmt(GSupplicantNetwork *network);
 
 struct _GSupplicantCallbacks {
        void (*system_ready) (void);
index f56b595ff56ba7480c8974eaae4d59e8be43b72d..470d99eb36b19b244942e70124fb1fea20f667e2 100644 (file)
@@ -92,6 +92,7 @@ static struct strvalmap keymgmt_map[] = {
        { "wpa-eap",            G_SUPPLICANT_KEYMGMT_WPA_EAP    },
        { "wpa-eap-sha256",     G_SUPPLICANT_KEYMGMT_WPA_EAP_256        },
        { "wps",                G_SUPPLICANT_KEYMGMT_WPS                },
+       { "sae",                G_SUPPLICANT_KEYMGMT_SAE                },
        { }
 };
 
@@ -234,6 +235,7 @@ struct _GSupplicantNetwork {
        unsigned int wps_capabilities;
        GHashTable *bss_table;
        GHashTable *config_table;
+       unsigned int keymgmt;
 };
 
 struct _GSupplicantPeer {
@@ -1215,7 +1217,7 @@ const char *g_supplicant_network_get_path(GSupplicantNetwork *network)
 const char *g_supplicant_network_get_mode(GSupplicantNetwork *network)
 {
        if (!network)
-               return G_SUPPLICANT_MODE_UNKNOWN;
+               return NULL;
 
        return mode2string(network->mode);
 }
@@ -1223,7 +1225,7 @@ const char *g_supplicant_network_get_mode(GSupplicantNetwork *network)
 const char *g_supplicant_network_get_security(GSupplicantNetwork *network)
 {
        if (!network)
-               return G_SUPPLICANT_SECURITY_UNKNOWN;
+               return NULL;
 
        return security2string(network->security);
 }
@@ -1427,6 +1429,14 @@ bool g_supplicant_peer_has_requested_connection(GSupplicantPeer *peer)
        return peer->connection_requested;
 }
 
+unsigned int g_supplicant_network_get_keymgmt(GSupplicantNetwork *network)
+{
+       if (!network)
+               return 0;
+
+       return network->keymgmt;
+}
+
 static void merge_network(GSupplicantNetwork *network)
 {
        GString *str;
@@ -1457,7 +1467,8 @@ static void merge_network(GSupplicantNetwork *network)
        else if (g_strcmp0(mode, "1") == 0)
                g_string_append_printf(str, "_adhoc");
 
-       if (g_strcmp0(key_mgmt, "WPA-PSK") == 0)
+       if ((g_strcmp0(key_mgmt, "WPA-PSK") == 0) ||
+           (g_strcmp0(key_mgmt, "SAE") == 0))
                g_string_append_printf(str, "_psk");
 
        group = g_string_free(str, FALSE);
@@ -1650,6 +1661,7 @@ static int add_or_replace_bss_to_network(struct g_supplicant_bss *bss)
        network->name = create_name(bss->ssid, bss->ssid_len);
        network->mode = bss->mode;
        network->security = bss->security;
+       network->keymgmt = bss->keymgmt;
        network->ssid_len = bss->ssid_len;
        memcpy(network->ssid, bss->ssid, bss->ssid_len);
        network->signal = bss->signal;
@@ -1931,7 +1943,8 @@ static void bss_compute_security(struct g_supplicant_bss *bss)
        if (bss->keymgmt &
                        (G_SUPPLICANT_KEYMGMT_WPA_PSK |
                                G_SUPPLICANT_KEYMGMT_WPA_FT_PSK |
-                               G_SUPPLICANT_KEYMGMT_WPA_PSK_256))
+                               G_SUPPLICANT_KEYMGMT_WPA_PSK_256 |
+                               G_SUPPLICANT_KEYMGMT_SAE))
                bss->psk = TRUE;
 
        if (bss->ieee8021x)
@@ -4890,8 +4903,16 @@ static void add_network_security_proto(DBusMessageIter *dict,
        g_free(proto);
 }
 
+static void add_network_ieee80211w(DBusMessageIter *dict, GSupplicantSSID *ssid,
+                                  GSupplicantMfpOptions ieee80211w)
+{
+       supplicant_dbus_dict_append_basic(dict, "ieee80211w", DBUS_TYPE_UINT32,
+                                         &ieee80211w);
+}
+
 static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid)
 {
+       GSupplicantMfpOptions ieee80211w;
        char *key_mgmt;
 
        switch (ssid->security) {
@@ -4907,7 +4928,22 @@ static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid)
                add_network_security_ciphers(dict, ssid);
                break;
        case G_SUPPLICANT_SECURITY_PSK:
-               key_mgmt = "WPA-PSK";
+               if (ssid->keymgmt & G_SUPPLICANT_KEYMGMT_SAE) {
+                       if (ssid->keymgmt & G_SUPPLICANT_KEYMGMT_WPA_PSK) {
+                               /*
+                                * WPA3-Personal transition mode: supports both
+                                * WPA2-Personal (PSK) and WPA3-Personal (SAE)
+                                */
+                               key_mgmt = "SAE WPA-PSK";
+                               ieee80211w = G_SUPPLICANT_MFP_OPTIONAL;
+                       } else {
+                               key_mgmt = "SAE";
+                               ieee80211w = G_SUPPLICANT_MFP_REQUIRED;
+                       }
+                       add_network_ieee80211w(dict, ssid, ieee80211w);
+               } else {
+                       key_mgmt = "WPA-PSK";
+               }
                add_network_security_psk(dict, ssid);
                add_network_security_ciphers(dict, ssid);
                add_network_security_proto(dict, ssid);
index 7508a9a1fb211541cc26d2e30326ee4a456d29d0..fcd658e8c398767058ba952a49544f6f20cd8a0b 100644 (file)
@@ -44,8 +44,10 @@ void connman_technology_regdom_notify(struct connman_technology *technology,
 
 enum connman_service_type connman_technology_get_type
                                (struct connman_technology *technology);
-bool connman_technology_get_wifi_tethering(const char **ssid,
-                                                       const char **psk);
+
+bool connman_technology_get_wifi_tethering(const struct connman_technology *technology,
+                                       const char **ssid, const char **psk, int *freq);
+
 bool connman_technology_is_tethering_allowed(enum connman_service_type type);
 
 struct connman_technology_driver {
@@ -60,7 +62,6 @@ struct connman_technology_driver {
        void (*remove_interface) (struct connman_technology *technology,
                                                                int index);
        int (*set_tethering) (struct connman_technology *technology,
-                               const char *identifier, const char *passphrase,
                                const char *bridge, bool enabled);
        int (*set_regdom) (struct connman_technology *technology,
                                                const char *alpha2);
index 5336103451fc40908b0aac9b3f1dda2143abe831..57cc8a292756f164ddad035f3b046f05a108aa4b 100644 (file)
@@ -882,7 +882,6 @@ static void bluetooth_tech_remove(struct connman_technology *technology)
 }
 
 static int bluetooth_tech_set_tethering(struct connman_technology *technology,
-               const char *identifier, const char *passphrase,
                const char *bridge, bool enabled)
 {
        GHashTableIter hash_iter;
index 6146b1c07c650dbd98338cc3284d1313a2f65bbe..0bf7fc41d39a3aa7548c2fce5cc385241b2bde84 100644 (file)
@@ -407,7 +407,6 @@ static void eth_tech_disable_tethering(struct connman_technology *technology,
 }
 
 static int eth_tech_set_tethering(struct connman_technology *technology,
-                               const char *identifier, const char *passphrase,
                                const char *bridge, bool enabled)
 {
        if (!connman_technology_is_tethering_allowed(
index 1b44bbb581332e088fb296c2268dd7dd053ffd30..2d58df3e93efa7e11eb45abb6ebec4456b7b4478 100644 (file)
@@ -294,7 +294,6 @@ static void gadget_tech_disable_tethering(struct connman_technology *technology,
 }
 
 static int gadget_tech_set_tethering(struct connman_technology *technology,
-                               const char *identifier, const char *passphrase,
                                const char *bridge, bool enabled)
 {
        DBG("bridge %s enabled %d", bridge, enabled);
index 4ba107f3e1130ae3f4f1f4d6540edae287f6f14f..ac3d1e1787ad43afd0897b165efee00c20bda65d 100644 (file)
@@ -209,18 +209,13 @@ static int cm_network_probe(struct connman_network *network)
 static void update_network_connected(struct iwd_network *iwdn)
 {
        struct iwd_device *iwdd;
-       int index;
 
        iwdd = g_hash_table_lookup(devices, iwdn->device);
        if (!iwdd)
                return;
 
-       index = connman_inet_ifindex(iwdd->name);
-       if (index < 0)
-               return;
-
-       DBG("interface name %s index %d", iwdd->name, index);
-       connman_network_set_index(iwdn->network, index);
+       DBG("interface name %s index %d", iwdd->name,
+               connman_network_get_index(iwdn->network));
        connman_network_set_connected(iwdn->network, true);
 }
 
@@ -316,11 +311,13 @@ static int cm_network_disconnect(struct connman_network *network)
        if (!iwds)
                return -EIO;
 
+       connman_network_set_associating(network, false);
+
        if (!g_dbus_proxy_method_call(iwds->proxy, "Disconnect",
                        NULL, cm_network_disconnect_cb, g_strdup(iwdn->path), g_free))
                return -EIO;
 
-       return -EINPROGRESS;
+       return 0;
 }
 
 struct auto_connect_cb_data {
@@ -558,7 +555,7 @@ static void cm_device_scan_cb(DBusMessage *message, void *user_data)
        const char *path = user_data;
        struct iwd_station *iwds;
 
-       iwds = g_hash_table_lookup(networks, path);
+       iwds = g_hash_table_lookup(stations, path);
        if (!iwds)
                return;
 
@@ -818,12 +815,16 @@ static int cm_change_tethering(struct iwd_device *iwdd,
 }
 
 static int cm_tech_tethering(struct connman_technology *technology,
-                       const char *identifier, const char *passphrase,
                        const char *bridge, bool enabled)
 {
        GHashTableIter iter;
        gpointer key, value;
        int err = 0, res;
+       const char *ssid;
+       const char *psk;
+       int freq;
+
+       connman_technology_get_wifi_tethering(technology, &ssid, &psk, &freq);
 
        g_hash_table_iter_init(&iter, devices);
 
@@ -840,8 +841,8 @@ static int cm_tech_tethering(struct connman_technology *technology,
                        continue;
 
                if (!enabled && !g_strcmp0("ap", iwdd->mode)) {
-                       res = cm_change_tethering(iwdd, technology, identifier,
-                                               passphrase, bridge, enabled);
+                       res = cm_change_tethering(iwdd, technology, ssid,
+                                               psk, bridge, enabled);
                        if (res)
                                connman_warn("%s switching to Station mode failed",
                                        iwdd->path);
@@ -851,8 +852,8 @@ static int cm_tech_tethering(struct connman_technology *technology,
                }
 
                if (enabled && !g_strcmp0("station", iwdd->mode)) {
-                       err = cm_change_tethering(iwdd, technology, identifier,
-                                       passphrase, bridge, enabled);
+                       err = cm_change_tethering(iwdd, technology, ssid,
+                                       psk, bridge, enabled);
                        if (err)
                                connman_warn("%s switching to AccessPoint mode failed",
                                        iwdd->path);
@@ -912,6 +913,7 @@ static void add_network(const char *path, struct iwd_network *iwdn)
 {
        struct iwd_device *iwdd;
        char *identifier;
+       int index;
 
        iwdd = g_hash_table_lookup(devices, iwdn->device);
        if (!iwdd)
@@ -920,6 +922,11 @@ static void add_network(const char *path, struct iwd_network *iwdn)
        identifier = create_identifier(path, iwdn->type);
        iwdn->network = connman_network_create(identifier,
                                        CONNMAN_NETWORK_TYPE_WIFI);
+
+       index = connman_inet_ifindex(iwdd->name);
+       if (index >= 0)
+               connman_network_set_index(iwdn->network, index);
+
        connman_network_set_data(iwdn->network, iwdn);
 
        connman_network_set_name(iwdn->network, iwdn->name);
@@ -936,7 +943,9 @@ static void add_network(const char *path, struct iwd_network *iwdn)
        }
        iwdn->iwdd = iwdd;
 
-       connman_network_set_available(iwdn->network, true);
+       if (connman_network_get_strength(iwdn->network))
+               connman_network_set_available(iwdn->network, true);
+
        connman_network_set_group(iwdn->network, identifier);
 
        g_free(identifier);
@@ -1372,13 +1381,7 @@ static void create_adapter(GDBusProxy *proxy)
        struct iwd_adapter *iwda;
        GSList *modes, *list;
 
-       iwda = g_try_new0(struct iwd_adapter, 1);
-
-       if (!iwda) {
-               connman_error("Out of memory creating IWD adapter");
-               return;
-       }
-
+       iwda = g_new0(struct iwd_adapter, 1);
        iwda->path = g_strdup(path);
        g_hash_table_replace(adapters, iwda->path, iwda);
 
@@ -1423,13 +1426,7 @@ static void create_device(GDBusProxy *proxy)
        const char *path = g_dbus_proxy_get_path(proxy);
        struct iwd_device *iwdd;
 
-       iwdd = g_try_new0(struct iwd_device, 1);
-
-       if (!iwdd) {
-               connman_error("Out of memory creating IWD device");
-               return;
-       }
-
+       iwdd = g_new0(struct iwd_device, 1);
        iwdd->path = g_strdup(path);
        g_hash_table_replace(devices, iwdd->path, iwdd);
 
@@ -1494,6 +1491,8 @@ static DBusMessage *agent_request_passphrase(DBusConnection *dbus_conn,
                return get_reply_on_error(message, EINVAL);
 
        passwd = connman_network_get_string(iwdn->network, "WiFi.Passphrase");
+       if (!passwd)
+               return get_reply_on_error(message, ENOKEY);
 
        return g_dbus_create_reply(message, DBUS_TYPE_STRING, &passwd,
                                        DBUS_TYPE_INVALID);
@@ -1596,13 +1595,7 @@ static void create_network(GDBusProxy *proxy)
        const char *path = g_dbus_proxy_get_path(proxy);
        struct iwd_network *iwdn;
 
-       iwdn = g_try_new0(struct iwd_network, 1);
-
-       if (!iwdn) {
-               connman_error("Out of memory creating IWD network");
-               return;
-       }
-
+       iwdn = g_new0(struct iwd_network, 1);
        iwdn->path = g_strdup(path);
        g_hash_table_replace(networks, iwdn->path, iwdn);
 
@@ -1682,12 +1675,7 @@ static void create_know_network(GDBusProxy *proxy)
        const char *path = g_dbus_proxy_get_path(proxy);
        struct iwd_known_network *iwdkn;
 
-       iwdkn = g_try_new0(struct iwd_known_network, 1);
-       if (!iwdkn) {
-               connman_error("Out of memory creating IWD known network");
-               return;
-       }
-
+       iwdkn = g_new0(struct iwd_known_network, 1);
        iwdkn->path = g_strdup(path);
        g_hash_table_replace(known_networks, iwdkn->path, iwdkn);
 
@@ -1721,12 +1709,7 @@ static void create_station(GDBusProxy *proxy)
        const char *path = g_dbus_proxy_get_path(proxy);
        struct iwd_station *iwds;
 
-       iwds = g_try_new0(struct iwd_station, 1);
-       if (!iwds) {
-               connman_error("Out of memory creating IWD station");
-               return;
-       }
-
+       iwds = g_new0(struct iwd_station, 1);
        iwds->path = g_strdup(path);
        g_hash_table_replace(stations, iwds->path, iwds);
 
@@ -1754,11 +1737,7 @@ static void create_ap(GDBusProxy *proxy)
        const char *path = g_dbus_proxy_get_path(proxy);
        struct iwd_ap *iwdap;
 
-       iwdap = g_try_new0(struct iwd_ap, 1);
-       if (!iwdap) {
-               connman_error("Out of memory creating IWD access point");
-               return;
-       }
+       iwdap = g_new0(struct iwd_ap, 1);
        iwdap->index = -1;
 
        iwdap->path = g_strdup(path);
index 45effd44000f212b0f54aff96ac29343b983d365..caaea8535215028e80e8de3d9754db67f63944f2 100644 (file)
@@ -223,9 +223,9 @@ static DBusMessage *create_request_oob_reply(DBusMessage *message)
        DBusMessageIter dict;
        const char *ssid, *psk;
        uint8_t *tlv_msg;
-       int length;
+       int length, freq;
 
-       if (!connman_technology_get_wifi_tethering(&ssid, &psk))
+       if (!connman_technology_get_wifi_tethering(NULL, &ssid, &psk, &freq))
                return get_reply_on_error(message, ENOTSUP);
 
        tlv_msg = encode_to_tlv(ssid, psk, &length);
index d708d1ffd25a223232fe77b6c737c01fad55cb38..42396d2a217c609e92a75409caf62769890512f4 100644 (file)
@@ -3,6 +3,7 @@
  *  Connection Manager
  *
  *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2019-2021  Jolla Ltd. 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
@@ -491,6 +492,9 @@ static int errorstr2val(const char *error) {
        if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".AlreadyConnected") == 0)
                return -EISCONN;
 
+       if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".NoCarrier") == 0)
+               return -ENOLINK;
+
        if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".OperationCanceled") == 0)
                return -ECANCELED;
 
@@ -529,16 +533,23 @@ static void connect_reply(DBusPendingCall *call, void *user_data)
        if (dbus_set_error_from_message(&error, reply)) {
                int err = errorstr2val(error.name);
 
+               switch (err) {
+               case -EINPROGRESS:
+                       break;
                /*
                 * ECANCELED means that user has canceled authentication
                 * dialog. That's not really an error, it's part of a normal
                 * workflow. We also take it as a request to turn autoconnect
                 * off, in case if it was on.
                 */
-               if (err == -ECANCELED) {
+               case -ECANCELED:
                        DBG("%s connect canceled", data->path);
                        connman_provider_set_autoconnect(data->provider, false);
-               } else if (err != -EINPROGRESS) {
+                       break;
+               case -ENOLINK: /* vpnd reports that connmand is not online. */
+               case -EISCONN:
+               case -ECONNREFUSED:
+               default:
                        connman_error("Connect reply: %s (%s)", error.message,
                                                                error.name);
                        DBG("data %p cb_data %p", data, cb_data);
@@ -1016,7 +1027,7 @@ static int disconnect_provider(struct connection_data *data)
 
        if (data->disconnect_call) {
                DBG("already disconnecting");
-               return -EINVAL;
+               return -EALREADY;
        }
 
        message = dbus_message_new_method_call(VPN_SERVICE, data->path,
index e7014510d9592e180aa169438374b3feb6f37bf7..e947b169b75756a2227e6415239a414b9d5ea8c6 100644 (file)
@@ -166,6 +166,10 @@ struct wifi_data {
        int assoc_code;
 };
 
+struct wifi_network {
+       unsigned int keymgmt;
+};
+
 struct disconnect_data {
        struct wifi_data *wifi;
        struct connman_network *network;
@@ -179,7 +183,6 @@ static bool wfd_service_registered = false;
 
 static void start_autoscan(struct connman_device *device);
 static int tech_set_tethering(struct connman_technology *technology,
-                               const char *identifier, const char *passphrase,
                                const char *bridge, bool enabled);
 
 static int p2p_tech_probe(struct connman_technology *technology)
@@ -816,6 +819,7 @@ static void remove_networks(struct connman_device *device,
        for (list = wifi->networks; list; list = list->next) {
                struct connman_network *network = list->data;
 
+               g_free(connman_network_get_data(network));
                connman_device_remove_network(device, network);
                connman_network_unref(network);
        }
@@ -2158,6 +2162,7 @@ static GSupplicantSecurity network_security(const char *security)
 
 static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network)
 {
+       struct wifi_network *network_data = connman_network_get_data(network);
        const char *security;
 
        memset(ssid, 0, sizeof(*ssid));
@@ -2167,6 +2172,8 @@ static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network)
        ssid->scan_ssid = 1;
        security = connman_network_get_string(network, "WiFi.Security");
        ssid->security = network_security(security);
+       ssid->keymgmt = network_data->keymgmt;
+       ssid->ieee80211w = G_SUPPLICANT_MFP_OPTIONAL;
        ssid->passphrase = connman_network_get_string(network,
                                                "WiFi.Passphrase");
 
@@ -2783,8 +2790,6 @@ static void ap_create_fail(GSupplicantInterface *interface)
                wifi->tethering = false;
 
                ret = tech_set_tethering(wifi->tethering_param->technology,
-                               wifi->tethering_param->ssid->ssid,
-                               wifi->tethering_param->ssid->passphrase,
                                wifi->bridge, true);
 
                if ((ret == -EOPNOTSUPP) && (wifi_technology)) {
@@ -2813,6 +2818,7 @@ static void network_added(GSupplicantNetwork *supplicant_network)
        struct connman_network *network;
        GSupplicantInterface *interface;
        struct wifi_data *wifi;
+       struct wifi_network *network_data;
        const char *name, *identifier, *security, *group, *mode;
        const unsigned char *ssid;
        unsigned int ssid_len;
@@ -2861,8 +2867,15 @@ static void network_added(GSupplicantNetwork *supplicant_network)
                }
 
                wifi->networks = g_slist_prepend(wifi->networks, network);
+
+               network_data = g_new0(struct wifi_network, 1);
+               connman_network_set_data(network, network_data);
        }
 
+       network_data = connman_network_get_data(network);
+       network_data->keymgmt =
+               g_supplicant_network_get_keymgmt(supplicant_network);
+
        if (name && name[0] != '\0')
                connman_network_set_name(network, name);
 
@@ -2930,6 +2943,7 @@ static void network_removed(GSupplicantNetwork *network)
 
        wifi->networks = g_slist_remove(wifi->networks, connman_network);
 
+       g_free(connman_network_get_data(connman_network));
        connman_device_remove_network(wifi->device, connman_network);
        connman_network_unref(connman_network);
 }
@@ -3319,19 +3333,31 @@ static void tech_remove(struct connman_technology *technology)
        wifi_technology = NULL;
 }
 
-static GSupplicantSSID *ssid_ap_init(const char *ssid, const char *passphrase)
+static GSupplicantSSID *ssid_ap_init(const struct connman_technology *technology)
 {
        GSupplicantSSID *ap;
+       const char *ssid, *passphrase;
+       int freq;
+       bool ret;
 
        ap = g_try_malloc0(sizeof(GSupplicantSSID));
        if (!ap)
                return NULL;
 
+       ret = connman_technology_get_wifi_tethering(technology,
+                                               &ssid, &passphrase,
+                                               &freq);
+       if (ret == false)
+               return NULL;
+
        ap->mode = G_SUPPLICANT_MODE_MASTER;
        ap->ssid = ssid;
        ap->ssid_len = strlen(ssid);
        ap->scan_ssid = 0;
-       ap->freq = 2412;
+       if (freq)
+               ap->freq = freq;
+       else
+               ap->freq = 2412;
 
        if (!passphrase || strlen(passphrase) == 0) {
                ap->security = G_SUPPLICANT_SECURITY_NONE;
@@ -3441,8 +3467,7 @@ static void sta_remove_callback(int result,
 }
 
 static int enable_wifi_tethering(struct connman_technology *technology,
-                               const char *bridge, const char *identifier,
-                               const char *passphrase, bool available)
+                               const char *bridge, bool available)
 {
        GList *list;
        GSupplicantInterface *interface;
@@ -3495,14 +3520,14 @@ static int enable_wifi_tethering(struct connman_technology *technology,
                info->wifi = wifi;
                info->technology = technology;
                info->wifi->bridge = bridge;
-               info->ssid = ssid_ap_init(identifier, passphrase);
+               info->ssid = ssid_ap_init(technology);
                if (!info->ssid)
                        goto failed;
 
                info->ifname = g_strdup(ifname);
 
                wifi->tethering_param->technology = technology;
-               wifi->tethering_param->ssid = ssid_ap_init(identifier, passphrase);
+               wifi->tethering_param->ssid = ssid_ap_init(technology);
                if (!wifi->tethering_param->ssid)
                        goto failed;
 
@@ -3544,7 +3569,6 @@ static int enable_wifi_tethering(struct connman_technology *technology,
 }
 
 static int tech_set_tethering(struct connman_technology *technology,
-                               const char *identifier, const char *passphrase,
                                const char *bridge, bool enabled)
 {
        GList *list;
@@ -3572,13 +3596,11 @@ static int tech_set_tethering(struct connman_technology *technology,
        }
 
        DBG("trying tethering for available devices");
-       err = enable_wifi_tethering(technology, bridge, identifier, passphrase,
-                               true);
+       err = enable_wifi_tethering(technology, bridge, true);
 
        if (err < 0) {
                DBG("trying tethering for any device");
-               err = enable_wifi_tethering(technology, bridge, identifier,
-                                       passphrase, false);
+               err = enable_wifi_tethering(technology, bridge, false);
        }
 
        return err;
index d6113af745fa8237167239ae97c95c59636c65c5..d4f9add478fba5bb0688cabcb261a1f1751a625b 100644 (file)
@@ -366,9 +366,9 @@ static void report_error_reply(DBusMessage *reply, void *user_data)
                        retry = true;
        }
 
+out:
        report_error->callback(report_error->user_context, retry,
                        report_error->user_data);
-out:
        g_free(report_error);
 }
 
index 62023b1072da7ef5905717174de5e04cdacca1d0..33fdc7375c15ccd4599d5098ca2b6f7f9bdfceba 100644 (file)
@@ -1106,8 +1106,7 @@ static char *config_pem_fsid(const char *pem_file)
 
 static void provision_service_wifi(struct connman_config_service *config,
                                struct connman_service *service,
-                               struct connman_network *network,
-                               const void *ssid, unsigned int ssid_len)
+                               struct connman_network *network)
 {
        if (config->eap)
                __connman_service_set_string(service, "EAP", config->eap);
@@ -1418,8 +1417,7 @@ static int try_provision_service(struct connman_config_service *config,
                                                config->timeservers);
 
        if (type == CONNMAN_SERVICE_TYPE_WIFI) {
-               provision_service_wifi(config, service, network,
-                                                       ssid, ssid_len);
+               provision_service_wifi(config, service, network);
        }
 
        __connman_service_mark_dirty();
index c454a581ed072e3a81bdb2ae7daef9bcaca13667..74b3157b3185fc43ab6ebd4e626069b01639f00c 100644 (file)
@@ -687,7 +687,7 @@ int __connman_dbus_init(DBusConnection *conn)
 
        connection = conn;
 
-       return 0;
+       return g_dbus_attach_object_manager(conn);
 }
 
 void __connman_dbus_cleanup(void)
index 38dbdd71e425b29b9f7986e23ef9a078f95ca6ba..cf1d36c74496f7f2b87053ed5a9df62540af25be 100644 (file)
@@ -1791,7 +1791,7 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                ulen = strlen(name) + 1;
                if ((uptr + ulen) > uncomp_end)
                        goto out;
-               strncpy(uptr, name, ulen);
+               memcpy(uptr, name, ulen);
 
                debug("pos %d ulen %d left %d name %s", pos, ulen,
                        (int)(uncomp_end - (uptr + ulen)), uptr);
@@ -1951,6 +1951,12 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
 
        if (offset < 0)
                return offset;
+       if (reply_len < 0)
+               return -EINVAL;
+       if (reply_len < offset + 1)
+               return -EINVAL;
+       if ((size_t)reply_len < sizeof(struct domain_hdr))
+               return -EINVAL;
 
        hdr = (void *)(reply + offset);
        dns_id = reply[offset] | reply[offset + 1] << 8;
@@ -1986,23 +1992,31 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                 */
                if (req->append_domain && ntohs(hdr->qdcount) == 1) {
                        uint16_t domain_len = 0;
-                       uint16_t header_len;
+                       uint16_t header_len, payload_len;
                        uint16_t dns_type, dns_class;
                        uint8_t host_len, dns_type_pos;
                        char uncompressed[NS_MAXDNAME], *uptr;
                        char *ptr, *eom = (char *)reply + reply_len;
+                       char *domain;
 
                        /*
                         * ptr points to the first char of the hostname.
                         * ->hostname.domain.net
                         */
                        header_len = offset + sizeof(struct domain_hdr);
+                       if (reply_len < header_len)
+                               return -EINVAL;
+                       payload_len = reply_len - header_len;
+
                        ptr = (char *)reply + header_len;
 
                        host_len = *ptr;
+                       domain = ptr + 1 + host_len;
+                       if (domain > eom)
+                               return -EINVAL;
+
                        if (host_len > 0)
-                               domain_len = strnlen(ptr + 1 + host_len,
-                                               reply_len - header_len);
+                               domain_len = strnlen(domain, eom - domain);
 
                        /*
                         * If the query type is anything other than A or AAAA,
@@ -2011,6 +2025,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                         */
                        dns_type_pos = host_len + 1 + domain_len + 1;
 
+                       if (ptr + (dns_type_pos + 3) > eom)
+                               return -EINVAL;
                        dns_type = ptr[dns_type_pos] << 8 |
                                                        ptr[dns_type_pos + 1];
                        dns_class = ptr[dns_type_pos + 2] << 8 |
@@ -2040,6 +2056,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                                int new_len, fixed_len;
                                char *answers;
 
+                               if (len > payload_len)
+                                       return -EINVAL;
                                /*
                                 * First copy host (without domain name) into
                                 * tmp buffer.
@@ -2054,6 +2072,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                                 * Copy type and class fields of the question.
                                 */
                                ptr += len + domain_len + 1;
+                               if (ptr + NS_QFIXEDSZ > eom)
+                                       return -EINVAL;
                                memcpy(uptr, ptr, NS_QFIXEDSZ);
 
                                /*
@@ -2063,6 +2083,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                                uptr += NS_QFIXEDSZ;
                                answers = uptr;
                                fixed_len = answers - uncompressed;
+                               if (ptr + offset > eom)
+                                       return -EINVAL;
 
                                /*
                                 * We then uncompress the result to buffer
@@ -2166,6 +2188,9 @@ out:
                        err = sendto(sk, req->resp, req->resplen, 0,
                                &req->sa, req->sa_len);
        } else {
+               uint16_t tcp_len = htons(req->resplen - 2);
+               /* correct TCP message length */
+               memcpy(req->resp, &tcp_len, sizeof(tcp_len));
                sk = req->client_sk;
                err = send(sk, req->resp, req->resplen, MSG_NOSIGNAL);
        }
@@ -2254,8 +2279,7 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
 
        len = recv(sk, buf, sizeof(buf), 0);
 
-       if (len >= 12)
-               forward_dns_reply(buf, len, IPPROTO_UDP, data);
+       forward_dns_reply(buf, len, IPPROTO_UDP, data);
 
        return TRUE;
 }
@@ -2336,14 +2360,21 @@ hangup:
                        }
                }
 
+               /*
+                * Remove the G_IO_OUT flag from the watch, otherwise we end
+                * up in a busy loop, because the socket is constantly writable.
+                *
+                * There seems to be no better way in g_io to do that than
+                * re-adding the watch.
+                */
+               g_source_remove(server->watch);
+               server->watch = g_io_add_watch(server->channel,
+                       G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+                       tcp_server_event, server);
+
                server->connected = true;
                server_list = g_slist_append(server_list, server);
 
-               if (server->timeout > 0) {
-                       g_source_remove(server->timeout);
-                       server->timeout = 0;
-               }
-
                for (list = request_list; list; ) {
                        struct request_data *req = list->data;
                        int status;
@@ -2707,7 +2738,7 @@ static void update_domain(int index, const char *domain, bool append)
        for (list = server_list; list; list = list->next) {
                struct server_data *data = list->data;
                GList *dom_list;
-               char *dom;
+               char *dom = NULL;
                bool dom_found = false;
 
                if (data->index < 0)
index df94d1eebaa79fd282b28d8af659ef3f269568ac..4039a73ca498f4a248a5d033e84c32d5b199244a 100644 (file)
@@ -1836,13 +1836,6 @@ int __connman_inet_ipv6_send_rs(int index, int timeout,
        return 0;
 }
 
-static inline void ipv6_addr_advert_mult(const struct in6_addr *addr,
-                                       struct in6_addr *advert)
-{
-       ipv6_addr_set(advert, htonl(0xFF020000), 0, htonl(0x2),
-                       htonl(0xFF000000) | addr->s6_addr32[3]);
-}
-
 #define MSG_SIZE_SEND 1452
 
 static int inc_len(int len, int inc)
index 1551826b9577a03e2a13e9ea781c7481cec8e4d8..34b1724a22bb0c831a4376ac85bc491e76eb7938 100644 (file)
@@ -1593,6 +1593,9 @@ static void disable_ipv6(struct connman_ipconfig *ipconfig)
 
        ifname = connman_inet_ifname(ipconfig->index);
 
+       if (!ifname)
+               return;
+
        set_ipv6_state(ifname, false);
 
        g_free(ifname);
@@ -1612,6 +1615,9 @@ static void enable_ipv6(struct connman_ipconfig *ipconfig)
 
        ifname = connman_inet_ifname(ipconfig->index);
 
+       if (!ifname)
+               return;
+
        if (ipconfig->method == CONNMAN_IPCONFIG_METHOD_AUTO)
                set_ipv6_privacy(ifname, ipconfig->ipv6_privacy_config);
 
index 6480caa629803a79aad45b2857ed45ff8f571c2d..e209cf261300f84c0e0d06a672952c032b25a12f 100644 (file)
@@ -44,6 +44,9 @@
 #define DEFAULT_INPUT_REQUEST_TIMEOUT (120 * 1000)
 #define DEFAULT_BROWSER_LAUNCH_TIMEOUT (300 * 1000)
 
+#define DEFAULT_ONLINE_CHECK_IPV4_URL "http://ipv4.connman.net/online/status.html"
+#define DEFAULT_ONLINE_CHECK_IPV6_URL "http://ipv6.connman.net/online/status.html"
+
 /*
  * We set the integer to 1 sec so that we have a chance to get
  * necessary IPv6 router advertisement messages that might have
@@ -97,6 +100,8 @@ static struct {
        char *vendor_class_id;
        bool enable_online_check;
        bool enable_online_to_ready_transition;
+       char *online_check_ipv4_url;
+       char *online_check_ipv6_url;
        unsigned int online_check_initial_interval;
        unsigned int online_check_max_interval;
        bool auto_connect_roaming_services;
@@ -122,6 +127,8 @@ static struct {
        .vendor_class_id = NULL,
        .enable_online_check = true,
        .enable_online_to_ready_transition = false,
+       .online_check_ipv4_url = NULL,
+       .online_check_ipv6_url = NULL,
        .online_check_initial_interval = DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL,
        .online_check_max_interval = DEFAULT_ONLINE_CHECK_MAX_INTERVAL,
        .auto_connect_roaming_services = false,
@@ -148,6 +155,8 @@ static struct {
 #define CONF_VENDOR_CLASS_ID            "VendorClassID"
 #define CONF_ENABLE_ONLINE_CHECK        "EnableOnlineCheck"
 #define CONF_ENABLE_ONLINE_TO_READY_TRANSITION "EnableOnlineToReadyTransition"
+#define CONF_ONLINE_CHECK_IPV4_URL      "OnlineCheckIPv4URL"
+#define CONF_ONLINE_CHECK_IPV6_URL      "OnlineCheckIPv6URL"
 #define CONF_ONLINE_CHECK_INITIAL_INTERVAL "OnlineCheckInitialInterval"
 #define CONF_ONLINE_CHECK_MAX_INTERVAL     "OnlineCheckMaxInterval"
 #define CONF_AUTO_CONNECT_ROAMING_SERVICES "AutoConnectRoamingServices"
@@ -174,6 +183,8 @@ static const char *supported_options[] = {
        CONF_VENDOR_CLASS_ID,
        CONF_ENABLE_ONLINE_CHECK,
        CONF_ENABLE_ONLINE_TO_READY_TRANSITION,
+       CONF_ONLINE_CHECK_IPV4_URL,
+       CONF_ONLINE_CHECK_IPV6_URL,
        CONF_ONLINE_CHECK_INITIAL_INTERVAL,
        CONF_ONLINE_CHECK_MAX_INTERVAL,
        CONF_AUTO_CONNECT_ROAMING_SERVICES,
@@ -341,6 +352,8 @@ static void parse_config(GKeyFile *config)
                connman_settings.auto_connect =
                        parse_service_types(default_auto_connect, CONF_ARRAY_SIZE(default_auto_connect));
 
+       g_strfreev(str_list);
+
        g_clear_error(&error);
 
        str_list = __connman_config_get_string_list(config, "General",
@@ -484,6 +497,27 @@ static void parse_config(GKeyFile *config)
                connman_settings.enable_online_to_ready_transition = boolean;
        }
 
+       g_clear_error(&error);
+
+       string = __connman_config_get_string(config, "General",
+                                       CONF_ONLINE_CHECK_IPV4_URL, &error);
+       if (!error)
+               connman_settings.online_check_ipv4_url = string;
+       else
+               connman_settings.online_check_ipv4_url =
+                       g_strdup(DEFAULT_ONLINE_CHECK_IPV4_URL);
+
+       g_clear_error(&error);
+
+       string = __connman_config_get_string(config, "General",
+                                       CONF_ONLINE_CHECK_IPV6_URL, &error);
+       if (!error)
+               connman_settings.online_check_ipv6_url = string;
+       else
+               connman_settings.online_check_ipv6_url =
+                       g_strdup(DEFAULT_ONLINE_CHECK_IPV6_URL);
+
+
        g_clear_error(&error);
 
        integer = g_key_file_get_integer(config, "General",
@@ -708,6 +742,12 @@ char *connman_setting_get_string(const char *key)
        if (g_str_equal(key, CONF_VENDOR_CLASS_ID))
                return connman_settings.vendor_class_id;
 
+       if (g_str_equal(key, CONF_ONLINE_CHECK_IPV4_URL))
+               return connman_settings.online_check_ipv4_url;
+
+       if (g_str_equal(key, CONF_ONLINE_CHECK_IPV6_URL))
+               return connman_settings.online_check_ipv6_url;
+
        if (g_strcmp0(key, "wifi") == 0) {
                if (!option_wifi)
                        return "nl80211,wext";
@@ -989,6 +1029,8 @@ int main(int argc, char *argv[])
        g_strfreev(connman_settings.blacklisted_interfaces);
        g_strfreev(connman_settings.tethering_technologies);
        g_free(connman_settings.vendor_class_id);
+       g_free(connman_settings.online_check_ipv4_url);
+       g_free(connman_settings.online_check_ipv6_url);
 
        g_free(option_debug);
        g_free(option_wifi);
index df70e178742a1c6af0d77581f8997752465b2453..ddd5799610da57bedd2a9cf606fb083d89ab7680 100644 (file)
 # Default value is true.
 # EnableOnlineCheck = false
 
+# Urls (IPv4 and IPv6 respectively) used during the online status check.
+# Please refer to the README for more detailed information.
+# Default values are http://ipv4.connman.net/online/status.html and
+# http://ipv6.connman.net/online/status.html respectively.
+# OnlineCheckIPv4URL= http://ipv4.connman.net/online/status.html
+# OnlineCheckIPv6URL= http://ipv6.connman.net/online/status.html
+
 # 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.
+# Please refer to the README for more detailed information.
+# Default values are 1 and 12 respectively.
 # OnlineCheckInitialInterval = 1
 # OnlineCheckMaxInterval = 12
 
index 3bf8f4e4da571c6ce679510644277365403ae949..892d3a42c39606438c0e18020db49acba5fb9841 100644 (file)
@@ -600,6 +600,9 @@ static const GDBusSignalTable manager_signals[] = {
        { GDBUS_SIGNAL("PeersChanged",
                        GDBUS_ARGS({ "changed", "a(oa{sv})" },
                                        { "removed", "ao" })) },
+       { GDBUS_SIGNAL("TetheringClientsChanged",
+                       GDBUS_ARGS({ "registered", "as" },
+                                       { "removed", "as" })) },
        { },
 };
 
index b12bbc091b0caee15ef0d9f277d9f1e221f841cf..1cbdf9cfed952cd3c285f56db7caff8d886461a6 100644 (file)
@@ -1797,8 +1797,6 @@ int __connman_network_connect(struct connman_network *network)
        if (!network->device)
                return -ENODEV;
 
-       __connman_device_disconnect(network->device);
-
        network->connecting = true;
 
        err = network->driver->connect(network);
index c9e84daba4582831a9f3f987b7184420faf977e2..a87296ffe9215e71442faa71fb24ae8c33ce8c9f 100644 (file)
@@ -1308,75 +1308,71 @@ static const char *type2string(uint16_t type)
 static GIOChannel *channel = NULL;
 static guint channel_watch = 0;
 
-struct rtnl_request {
-       struct nlmsghdr hdr;
-       struct rtgenmsg msg;
-};
-#define RTNL_REQUEST_SIZE  (sizeof(struct nlmsghdr) + sizeof(struct rtgenmsg))
+#define RTNL_REQUEST_SIZE (NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(struct rtgenmsg)))
 
 static GSList *request_list = NULL;
 static guint32 request_seq = 0;
 
-static struct rtnl_request *find_request(guint32 seq)
+static struct nlmsghdr *find_request(guint32 seq)
 {
        GSList *list;
 
        for (list = request_list; list; list = list->next) {
-               struct rtnl_request *req = list->data;
+               struct nlmsghdr *hdr = list->data;
 
-               if (req->hdr.nlmsg_seq == seq)
-                       return req;
+               if (hdr->nlmsg_seq == seq)
+                       return hdr;
        }
 
        return NULL;
 }
 
-static int send_request(struct rtnl_request *req)
+static int send_request(struct nlmsghdr *hdr)
 {
        struct sockaddr_nl addr;
        int sk;
 
        DBG("%s len %d type %d flags 0x%04x seq %d",
-                               type2string(req->hdr.nlmsg_type),
-                               req->hdr.nlmsg_len, req->hdr.nlmsg_type,
-                               req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+                               type2string(hdr->nlmsg_type),
+                               hdr->nlmsg_len, hdr->nlmsg_type,
+                               hdr->nlmsg_flags, hdr->nlmsg_seq);
 
        sk = g_io_channel_unix_get_fd(channel);
 
        memset(&addr, 0, sizeof(addr));
        addr.nl_family = AF_NETLINK;
 
-       return sendto(sk, req, req->hdr.nlmsg_len, 0,
+       return sendto(sk, hdr, hdr->nlmsg_len, 0,
                                (struct sockaddr *) &addr, sizeof(addr));
 }
 
-static int queue_request(struct rtnl_request *req)
+static int queue_request(struct nlmsghdr *hdr)
 {
-       request_list = g_slist_append(request_list, req);
+       request_list = g_slist_append(request_list, hdr);
 
        if (g_slist_length(request_list) > 1)
                return 0;
 
-       return send_request(req);
+       return send_request(hdr);
 }
 
 static int process_response(guint32 seq)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
 
        DBG("seq %d", seq);
 
-       req = find_request(seq);
-       if (req) {
-               request_list = g_slist_remove(request_list, req);
-               g_free(req);
+       hdr = find_request(seq);
+       if (hdr) {
+               request_list = g_slist_remove(request_list, hdr);
+               g_free(hdr);
        }
 
-       req = g_slist_nth_data(request_list, 0);
-       if (!req)
+       hdr = g_slist_nth_data(request_list, 0);
+       if (!hdr)
                return 0;
 
-       return send_request(req);
+       return send_request(hdr);
 }
 
 static void rtnl_message(void *buf, size_t len)
@@ -1474,62 +1470,65 @@ static gboolean netlink_event(GIOChannel *chan, GIOCondition cond, gpointer data
 
 static int send_getlink(void)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
+       struct rtgenmsg *msg;
 
        DBG("");
 
-       req = g_try_malloc0(RTNL_REQUEST_SIZE);
-       if (!req)
-               return -ENOMEM;
+       hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+       hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+       hdr->nlmsg_type = RTM_GETLINK;
+       hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       hdr->nlmsg_pid = 0;
+       hdr->nlmsg_seq = request_seq++;
 
-       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
-       req->hdr.nlmsg_type = RTM_GETLINK;
-       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-       req->hdr.nlmsg_pid = 0;
-       req->hdr.nlmsg_seq = request_seq++;
-       req->msg.rtgen_family = AF_INET;
+       msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+       msg->rtgen_family = AF_INET;
 
-       return queue_request(req);
+       return queue_request(hdr);
 }
 
 static int send_getaddr(void)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
+       struct rtgenmsg *msg;
 
        DBG("");
 
-       req = g_try_malloc0(RTNL_REQUEST_SIZE);
-       if (!req)
-               return -ENOMEM;
+       hdr = g_malloc0(RTNL_REQUEST_SIZE);
 
-       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
-       req->hdr.nlmsg_type = RTM_GETADDR;
-       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-       req->hdr.nlmsg_pid = 0;
-       req->hdr.nlmsg_seq = request_seq++;
-       req->msg.rtgen_family = AF_INET;
+       hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+       hdr->nlmsg_type = RTM_GETADDR;
+       hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       hdr->nlmsg_pid = 0;
+       hdr->nlmsg_seq = request_seq++;
 
-       return queue_request(req);
+       msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+       msg->rtgen_family = AF_INET;
+
+       return queue_request(hdr);
 }
 
 static int send_getroute(void)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
+       struct rtgenmsg *msg;
 
        DBG("");
 
-       req = g_try_malloc0(RTNL_REQUEST_SIZE);
-       if (!req)
-               return -ENOMEM;
+       hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+       hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+       hdr->nlmsg_type = RTM_GETROUTE;
+       hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       hdr->nlmsg_pid = 0;
+       hdr->nlmsg_seq = request_seq++;
 
-       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
-       req->hdr.nlmsg_type = RTM_GETROUTE;
-       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-       req->hdr.nlmsg_pid = 0;
-       req->hdr.nlmsg_seq = request_seq++;
-       req->msg.rtgen_family = AF_INET;
+       msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+       msg->rtgen_family = AF_INET;
 
-       return queue_request(req);
+       return queue_request(hdr);
 }
 
 static gboolean update_timeout_cb(gpointer user_data)
@@ -1673,14 +1672,14 @@ void __connman_rtnl_cleanup(void)
        update_list = NULL;
 
        for (list = request_list; list; list = list->next) {
-               struct rtnl_request *req = list->data;
+               struct nlmsghdr *hdr= list->data;
 
                DBG("%s len %d type %d flags 0x%04x seq %d",
-                               type2string(req->hdr.nlmsg_type),
-                               req->hdr.nlmsg_len, req->hdr.nlmsg_type,
-                               req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+                               type2string(hdr->nlmsg_type),
+                               hdr->nlmsg_len, hdr->nlmsg_type,
+                               hdr->nlmsg_flags, hdr->nlmsg_seq);
 
-               g_free(req);
+               g_free(hdr);
                list->data = NULL;
        }
 
index 20917a8923a411da53e95f785f43d481f70e9427..1d2b78a675310d71c81d32395144652a2ce4ab7d 100644 (file)
@@ -49,6 +49,7 @@ static DBusConnection *connection = NULL;
 
 static GList *service_list = NULL;
 static GHashTable *service_hash = NULL;
+static GHashTable *passphrase_requested = NULL;
 static GSList *counter_list = NULL;
 static unsigned int autoconnect_id = 0;
 static unsigned int vpn_autoconnect_id = 0;
@@ -1594,6 +1595,25 @@ bool __connman_service_index_is_default(int index)
        return __connman_service_get_index(service) == index;
 }
 
+static void start_wispr_when_connected(struct connman_service *service)
+{
+       if (!connman_setting_get_bool("EnableOnlineCheck")) {
+               connman_info("Online check disabled. "
+                       "Default service remains in READY state.");
+               return;
+       }
+
+       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);
+}
+
 static void default_changed(void)
 {
        struct connman_service *service = connman_service_get_default();
@@ -1618,15 +1638,7 @@ 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);
+               start_wispr_when_connected(service);
 
                /*
                 * Connect VPN automatically when new default service
@@ -3725,13 +3737,7 @@ static DBusMessage *set_property(DBusConnection *conn,
                nameserver_add_all(service, CONNMAN_IPCONFIG_TYPE_ALL);
                dns_configuration_changed(service);
 
-               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);
+               start_wispr_when_connected(service);
 
                service_save(service);
        } else if (g_str_equal(name, "Timeservers.Configuration")) {
@@ -4209,6 +4215,7 @@ static bool auto_connect_service(GList *services,
        bool ignore[MAX_CONNMAN_SERVICE_TYPES] = { };
        bool autoconnecting = false;
        GList *list;
+       int index;
 
        DBG("preferred %d sessions %d reason %s", preferred, active_count,
                reason2string(reason));
@@ -4230,6 +4237,11 @@ static bool auto_connect_service(GList *services,
                        continue;
                }
 
+               index = __connman_service_get_index(service);
+               if (g_hash_table_lookup(passphrase_requested,
+                                       GINT_TO_POINTER(index)))
+                       return true;
+
                if (service->pending ||
                                is_connecting(service->state) ||
                                is_connected(service->state)) {
@@ -4711,15 +4723,11 @@ static void apply_relevant_default_downgrade(struct connman_service *service)
        struct connman_service *def_service;
 
        def_service = connman_service_get_default();
-       if (!def_service)
+       if (!def_service || def_service != service ||
+               def_service->state != CONNMAN_SERVICE_STATE_ONLINE)
                return;
 
-       if (def_service == service &&
-                       def_service->state == CONNMAN_SERVICE_STATE_ONLINE) {
-               def_service->state = CONNMAN_SERVICE_STATE_READY;
-               __connman_notifier_leave_online(def_service->type);
-               state_changed(def_service);
-       }
+       downgrade_state(def_service);
 }
 
 static void switch_default_service(struct connman_service *default_service,
@@ -5316,6 +5324,25 @@ static gint service_compare_vpn(struct connman_service *a,
        return service_compare(transport, service);
 }
 
+static gint service_compare_preferred(struct connman_service *service_a,
+                                       struct connman_service *service_b)
+{
+       unsigned int *tech_array;
+       int i;
+
+       tech_array = connman_setting_get_uint_list("PreferredTechnologies");
+       if (tech_array) {
+               for (i = 0; tech_array[i]; i++) {
+                       if (tech_array[i] == service_a->type)
+                               return -1;
+
+                       if (tech_array[i] == service_b->type)
+                               return 1;
+               }
+       }
+       return 0;
+}
+
 static gint service_compare(gconstpointer a, gconstpointer b)
 {
        struct connman_service *service_a = (void *) a;
@@ -5346,6 +5373,10 @@ static gint service_compare(gconstpointer a, gconstpointer b)
 
                if (service_a->order < service_b->order)
                        return 1;
+
+               rval = service_compare_preferred(service_a, service_b);
+               if (rval)
+                       return rval;
        }
 
        if (state_a != state_b) {
@@ -5376,20 +5407,11 @@ static gint service_compare(gconstpointer a, gconstpointer b)
                return 1;
 
        if (service_a->type != service_b->type) {
-               unsigned int *tech_array;
-               int i;
-
-               tech_array = connman_setting_get_uint_list(
-                                               "PreferredTechnologies");
-               if (tech_array) {
-                       for (i = 0; tech_array[i]; i++) {
-                               if (tech_array[i] == service_a->type)
-                                       return -1;
+               int rval;
 
-                               if (tech_array[i] == service_b->type)
-                                       return 1;
-                       }
-               }
+               rval = service_compare_preferred(service_a, service_b);
+               if (rval)
+                       return rval;
 
                if (service_a->type == CONNMAN_SERVICE_TYPE_ETHERNET)
                        return -1;
@@ -5780,6 +5802,7 @@ static void request_input_cb(struct connman_service *service,
        struct connman_device *device;
        const char *security;
        int err = 0;
+       int index;
 
        DBG("RequestInput return, %p", service);
 
@@ -5842,6 +5865,10 @@ static void request_input_cb(struct connman_service *service,
                err = __connman_service_set_passphrase(service, passphrase);
 
  done:
+       index = __connman_service_get_index(service);
+       g_hash_table_remove(passphrase_requested,
+                               GINT_TO_POINTER(index));
+
        if (err >= 0) {
                /* We forget any previous error. */
                set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
@@ -5895,27 +5922,14 @@ static int service_update_preferred_order(struct connman_service *default_servic
                struct connman_service *new_service,
                enum connman_service_state new_state)
 {
-       unsigned int *tech_array;
-       int i;
-
-       if (!default_service || default_service == new_service ||
-                       default_service->state != new_state)
+       if (!default_service || default_service == new_service)
                return 0;
 
-       tech_array = connman_setting_get_uint_list("PreferredTechnologies");
-       if (tech_array) {
-
-               for (i = 0; tech_array[i] != 0; i += 1) {
-                       if (default_service->type == tech_array[i])
-                               return -EALREADY;
-
-                       if (new_service->type == tech_array[i]) {
-                               switch_default_service(default_service,
-                                               new_service);
-                               __connman_connection_update_gateway();
-                               return 0;
-                       }
-               }
+       if (service_compare_preferred(default_service, new_service) > 0) {
+               switch_default_service(default_service,
+                               new_service);
+               __connman_connection_update_gateway();
+               return 0;
        }
 
        return -EALREADY;
@@ -6009,6 +6023,15 @@ static int service_indicate_state(struct connman_service *service)
                break;
 
        case CONNMAN_SERVICE_STATE_IDLE:
+               if (old_state == CONNMAN_SERVICE_STATE_FAILURE &&
+                               service->connect_reason ==
+                                       CONNMAN_SERVICE_CONNECT_REASON_NATIVE &&
+                               service->error ==
+                                       CONNMAN_SERVICE_ERROR_INVALID_KEY) {
+                       __connman_service_clear_error(service);
+                       service_complete(service);
+               }
+
                if (old_state != CONNMAN_SERVICE_STATE_DISCONNECT)
                        __connman_service_disconnect(service);
 
@@ -6051,12 +6074,12 @@ static int service_indicate_state(struct connman_service *service)
 
                service->new_service = false;
 
-               default_changed();
-
                def_service = connman_service_get_default();
 
                service_update_preferred_order(def_service, service, new_state);
 
+               default_changed();
+
                __connman_service_set_favorite(service, true);
 
                reply_pending(service, 0);
@@ -6125,7 +6148,8 @@ static int service_indicate_state(struct connman_service *service)
                break;
 
        case CONNMAN_SERVICE_STATE_FAILURE:
-               if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER) {
+               if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER ||
+                       service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_NATIVE) {
                        connman_agent_report_error(service, service->path,
                                                error2string(service->error),
                                                report_error_cb,
@@ -6693,6 +6717,7 @@ static int service_connect(struct connman_service *service)
 int __connman_service_connect(struct connman_service *service,
                        enum connman_service_connect_reason reason)
 {
+       int index;
        int err;
 
        DBG("service %p state %s connect reason %s -> %s",
@@ -6756,7 +6781,8 @@ int __connman_service_connect(struct connman_service *service,
                                service->provider)
                        connman_provider_disconnect(service->provider);
 
-       if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER) {
+       if (reason == CONNMAN_SERVICE_CONNECT_REASON_USER ||
+                       reason == CONNMAN_SERVICE_CONNECT_REASON_NATIVE) {
                if (err == -ENOKEY || err == -EPERM) {
                        DBusMessage *pending = NULL;
                        const char *dbus_sender = get_dbus_sender(service);
@@ -6779,6 +6805,13 @@ int __connman_service_connect(struct connman_service *service,
                        if (service->hidden && err != -EINPROGRESS)
                                service->pending = pending;
 
+                       if (err == -EINPROGRESS) {
+                               index = __connman_service_get_index(service);
+                               g_hash_table_replace(passphrase_requested,
+                                               GINT_TO_POINTER(index),
+                                               GINT_TO_POINTER(true));
+                       }
+
                        return err;
                }
        }
@@ -7440,8 +7473,19 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
        if (__connman_network_get_weakness(network))
                return service;
 
+       index = connman_network_get_index(network);
+
        if (service->path) {
                update_from_network(service, network);
+
+               if (service->ipconfig_ipv4)
+                       __connman_ipconfig_set_index(service->ipconfig_ipv4,
+                                                                       index);
+
+               if (service->ipconfig_ipv6)
+                       __connman_ipconfig_set_index(service->ipconfig_ipv6,
+                                                                       index);
+
                __connman_connection_update_gateway();
                return service;
        }
@@ -7472,14 +7516,16 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
 
        update_from_network(service, network);
 
-       index = connman_network_get_index(network);
-
        if (!service->ipconfig_ipv4)
                service->ipconfig_ipv4 = create_ip4config(service, index,
                                CONNMAN_IPCONFIG_METHOD_DHCP);
+       else
+               __connman_ipconfig_set_index(service->ipconfig_ipv4, index);
 
        if (!service->ipconfig_ipv6)
                service->ipconfig_ipv6 = create_ip6config(service, index);
+       else
+               __connman_ipconfig_set_index(service->ipconfig_ipv6, index);
 
        service_register(service);
        service_schedule_added(service);
@@ -7750,6 +7796,8 @@ int __connman_service_init(void)
        service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                        NULL, service_free);
 
+       passphrase_requested = g_hash_table_new(g_direct_hash, g_direct_equal);
+
        services_notify = g_new0(struct _services_notify, 1);
        services_notify->remove = g_hash_table_new_full(g_str_hash,
                        g_str_equal, g_free, NULL);
@@ -7782,6 +7830,9 @@ void __connman_service_cleanup(void)
        g_hash_table_destroy(service_hash);
        service_hash = NULL;
 
+       g_hash_table_destroy(passphrase_requested);
+       passphrase_requested = NULL;
+
        g_slist_free(counter_list);
        counter_list = NULL;
 
index 672d6ea81990bc966908bc6e4f4a7b7469abb11c..5c469111e43a76226910516ce64289e14e70bd11 100644 (file)
@@ -66,6 +66,7 @@ struct connman_technology {
                                              */
        char *tethering_ident;
        char *tethering_passphrase;
+       int tethering_freq;
 
        bool enable_persistent; /* Save the tech state */
 
@@ -192,6 +193,13 @@ static void technology_save(struct connman_technology *technology)
                g_free(enc);
        }
 
+       if (technology->tethering_freq == 0)
+               technology->tethering_freq = 2412;
+
+       g_key_file_set_integer(keyfile, identifier,
+                               "Tethering.Freq",
+                               technology->tethering_freq);
+
 done:
        g_free(identifier);
 
@@ -264,8 +272,7 @@ static int set_tethering(struct connman_technology *technology,
                if (!driver || !driver->set_tethering)
                        continue;
 
-               err = driver->set_tethering(technology, ident, passphrase,
-                               bridge, enabled);
+               err = driver->set_tethering(technology, bridge, enabled);
 
                if (result == -EINPROGRESS)
                        continue;
@@ -356,25 +363,32 @@ enum connman_service_type connman_technology_get_type
        return technology->type;
 }
 
-bool connman_technology_get_wifi_tethering(const char **ssid,
-                                                       const char **psk)
+bool connman_technology_get_wifi_tethering(const struct connman_technology *technology,
+                                       const char **ssid, const char **psk,
+                                       int *freq)
 {
-       struct connman_technology *technology;
+       bool force = true;
 
        if (!ssid || !psk)
                return false;
 
        *ssid = *psk = NULL;
 
-       technology = technology_find(CONNMAN_SERVICE_TYPE_WIFI);
+       /* Workaround for the neard plugin */
+       if (!technology) {
+               technology = technology_find(CONNMAN_SERVICE_TYPE_WIFI);
+               force = false;
+       }
+
        if (!technology)
                return false;
 
-       if (!technology->tethering)
+       if (!force && !technology->tethering)
                return false;
 
        *ssid = technology->tethering_ident;
        *psk = technology->tethering_passphrase;
+       *freq = technology->tethering_freq;
 
        return true;
 }
@@ -443,6 +457,10 @@ static void technology_load(struct connman_technology *technology)
                                identifier, "Tethering.Passphrase", NULL);
        if (enc)
                technology->tethering_passphrase = g_strcompress(enc);
+
+       technology->tethering_freq = g_key_file_get_integer(keyfile,
+                               identifier, "Tethering.Freq", NULL);
+
 done:
        g_free(identifier);
 
@@ -554,6 +572,10 @@ static void append_properties(DBusMessageIter *iter,
                                        DBUS_TYPE_STRING,
                                        &technology->tethering_passphrase);
 
+       connman_dbus_dict_append_basic(&dict, "TetheringFreq",
+                               DBUS_TYPE_INT32,
+                               &technology->tethering_freq);
+
        connman_dbus_dict_close(iter, &dict);
 }
 
@@ -968,6 +990,27 @@ static DBusMessage *set_property(DBusConnection *conn,
                                        DBUS_TYPE_STRING,
                                        &technology->tethering_passphrase);
                }
+       } else if (g_str_equal(name, "TetheringFreq")) {
+               dbus_int32_t freq;
+
+               if (type != DBUS_TYPE_INT32)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &freq);
+
+               if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
+                       return __connman_error_not_supported(msg);
+
+               if (freq >= 0) {
+                       technology->tethering_freq = freq;
+                       technology_save(technology);
+
+                       connman_dbus_property_changed_basic(technology->path,
+                                       CONNMAN_TECHNOLOGY_INTERFACE,
+                                       "TetheringFreq",
+                                       DBUS_TYPE_INT32,
+                                       &technology->tethering_freq);
+               }
        } else if (g_str_equal(name, "Powered")) {
                dbus_bool_t enable;
 
index c63dc8195fde831fb1d10f294e8bf07beb666859..56007a379327bbfaec56887d4d1091e96efa82b5 100644 (file)
@@ -30,9 +30,6 @@
 
 #include "connman.h"
 
-#define STATUS_URL_IPV4  "http://ipv4.connman.net/online/status.html"
-#define STATUS_URL_IPV6  "http://ipv6.connman.net/online/status.html"
-
 struct connman_wispr_message {
        bool has_error;
        const char *current_element;
@@ -96,6 +93,8 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data);
 
 static GHashTable *wispr_portal_list = NULL;
 
+static char *online_check_ipv4_url = NULL;
+static char *online_check_ipv6_url = NULL;
 static bool enable_online_to_ready_transition = false;
 
 static void connman_wispr_message_init(struct connman_wispr_message *msg)
@@ -910,10 +909,10 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
 
        if (wp_context->type == CONNMAN_IPCONFIG_TYPE_IPV4) {
                g_web_set_address_family(wp_context->web, AF_INET);
-               wp_context->status_url = STATUS_URL_IPV4;
+               wp_context->status_url = online_check_ipv4_url;
        } else {
                g_web_set_address_family(wp_context->web, AF_INET6);
-               wp_context->status_url = STATUS_URL_IPV6;
+               wp_context->status_url = online_check_ipv6_url;
        }
 
        for (i = 0; nameservers[i]; i++)
@@ -1036,6 +1035,11 @@ int __connman_wispr_init(void)
                                                g_direct_equal, NULL,
                                                free_connman_wispr_portal);
 
+       online_check_ipv4_url =
+               connman_setting_get_string("OnlineCheckIPv4URL");
+       online_check_ipv6_url =
+               connman_setting_get_string("OnlineCheckIPv6URL");
+
        enable_online_to_ready_transition =
                connman_setting_get_bool("EnableOnlineToReadyTransition");
 
index 41e842dd64af4019fd529700acf8030c65254d8e..a52f4af04edfd950021540f13f3b8c6696f64203 100644 (file)
@@ -45,7 +45,7 @@ int main(int argc, char *argv[])
 {
        enum iptables_command cmd = IPTABLES_COMMAND_UNKNOWN;
        char *table = NULL, *chain = NULL, *rule = NULL, *tmp;
-       int err, c, i;
+       int err = -EINVAL, c, i;
 
        opterr = 0;
 
index e9b7cb224d7aeb86bcc72b9694f766d61273e0b8..f9d091eb9490427db194e3fba64beab059690d00 100644 (file)
@@ -44,7 +44,7 @@ int main(int argc, char *argv[])
 {
        enum iptables_command cmd = IPTABLES_COMMAND_UNKNOWN;
        char *table = NULL, *chain = NULL, *rule = NULL, *tmp;
-       int err, c, i;
+       int err = -EINVAL, c, i;
 
        opterr = 0;
 
index cd261d054132ee0370d9c1cfc726095511dc5e04..f08736ea5f0ef4191524e98b45dd63a70a6b9fd7 100644 (file)
@@ -69,13 +69,13 @@ static void set_test_config(enum configtype type)
 #define IP6T_SO_GET_INFO               (IP6T_BASE_CTL)
 #define IP6T_SO_GET_ENTRIES            (IP6T_BASE_CTL + 1)
 
-int xt_match_parse(int c, char **argv, int invert, unsigned int *flags,
+int static xt_match_parse(int c, char **argv, int invert, unsigned int *flags,
                        const void *entry, struct xt_entry_match **match)
 {
        return 0;
 }
 
-int xt_target_parse(int c, char **argv, int invert, unsigned int *flags,
+int static xt_target_parse(int c, char **argv, int invert, unsigned int *flags,
                        const void *entry, struct xt_entry_target **targetinfo)
 {
        return 0;
index 1e4fcd1f34052a18e077765c0b2731d9e5419688..ee40dd72b36107a5f41560d8d39c1537fe6eceba 100644 (file)
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2010,2013  BMW Car IT GmbH.
  *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2019-2021  Jolla Ltd.
  *
  *  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
@@ -121,12 +122,40 @@ struct {
 static DBusConnection *connection;
 
 struct l2tp_private_data {
+       struct vpn_provider *provider;
        struct connman_task *task;
        char *if_name;
        vpn_provider_connect_cb_t cb;
        void *user_data;
 };
 
+static void l2tp_connect_done(struct l2tp_private_data *data, int err)
+{
+       vpn_provider_connect_cb_t cb;
+       void *user_data;
+
+       if (!data || !data->cb)
+               return;
+
+       /* Ensure that callback is called only once */
+       cb = data->cb;
+       user_data = data->user_data;
+       data->cb = NULL;
+       data->user_data = NULL;
+       cb(data->provider, user_data, err);
+}
+
+static void free_private_data(struct l2tp_private_data *data)
+{
+       if (vpn_provider_get_plugin_data(data->provider) == data)
+               vpn_provider_set_plugin_data(data->provider, NULL);
+
+       l2tp_connect_done(data, EIO);
+       vpn_provider_unref(data->provider);
+       g_free(data->if_name);
+       g_free(data);
+}
+
 static DBusMessage *l2tp_get_sec(struct connman_task *task,
                        DBusMessage *msg, void *user_data)
 {
@@ -164,6 +193,9 @@ static int l2tp_notify(DBusMessage *msg, struct vpn_provider *provider)
        char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
        char *ifname = NULL, *nameservers = NULL;
        struct connman_ipaddress *ipaddress = NULL;
+       struct l2tp_private_data *data;
+
+       data = vpn_provider_get_plugin_data(provider);
 
        dbus_message_iter_init(msg, &iter);
 
@@ -182,11 +214,22 @@ static int l2tp_notify(DBusMessage *msg, struct vpn_provider *provider)
                vpn_provider_set_string_hide_value(provider, "L2TP.Password",
                                        NULL);
 
+               l2tp_connect_done(data, EACCES);
                return VPN_STATE_AUTH_FAILURE;
        }
 
-       if (strcmp(reason, "connect"))
+       if (strcmp(reason, "connect")) {
+               l2tp_connect_done(data, EIO);
+
+               /*
+                * Stop the task to avoid potential looping of this state when
+                * authentication fails.
+                */
+               if (data && data->task)
+                       connman_task_stop(data->task);
+
                return VPN_STATE_DISCONNECT;
+       }
 
        dbus_message_iter_recurse(&iter, &dict);
 
@@ -257,6 +300,8 @@ static int l2tp_notify(DBusMessage *msg, struct vpn_provider *provider)
        g_free(nameservers);
        connman_ipaddress_free(ipaddress);
 
+       l2tp_connect_done(data, 0);
+
        return VPN_STATE_CONNECT;
 }
 
@@ -476,9 +521,10 @@ static int l2tp_write_config(struct vpn_provider *provider,
 
 static void l2tp_died(struct connman_task *task, int exit_code, void *user_data)
 {
+       struct l2tp_private_data *data = user_data;
        char *conf_file;
 
-       vpn_died(task, exit_code, user_data);
+       vpn_died(task, exit_code, data->provider);
 
        conf_file = g_strdup_printf(VPN_STATEDIR "/connman-xl2tpd.conf");
        unlink(conf_file);
@@ -487,6 +533,8 @@ static void l2tp_died(struct connman_task *task, int exit_code, void *user_data)
        conf_file = g_strdup_printf(VPN_STATEDIR "/connman-ppp-option.conf");
        unlink(conf_file);
        g_free(conf_file);
+
+       free_private_data(data);
 }
 
 struct request_input_reply {
@@ -646,12 +694,12 @@ static int request_input(struct vpn_provider *provider,
        return -EINPROGRESS;
 }
 
-static int run_connect(struct vpn_provider *provider,
-                       struct connman_task *task, const char *if_name,
-                       vpn_provider_connect_cb_t cb, void *user_data,
+static int run_connect(struct l2tp_private_data *data,
                        const char *username, const char *password)
 {
-       char *l2tp_name, *pppd_name;
+       struct vpn_provider *provider = data->provider;
+       struct connman_task *task = data->task;
+       char *l2tp_name, *ctrl_name, *pppd_name;
        int l2tp_fd, pppd_fd;
        int err;
 
@@ -674,12 +722,24 @@ static int run_connect(struct vpn_provider *provider,
                goto done;
        }
 
-       pppd_name = g_strdup_printf(VPN_STATEDIR "/connman-ppp-option.conf");
+       ctrl_name = g_strconcat(VPN_STATEDIR, "/connman-xl2tpd-control", NULL);
+
+       if (mkfifo(ctrl_name, S_IRUSR|S_IWUSR) != 0 && errno != EEXIST) {
+               connman_error("Error creating xl2tp control pipe");
+               g_free(l2tp_name);
+               g_free(ctrl_name);
+               close(l2tp_fd);
+               err = -EIO;
+               goto done;
+       }
+
+       pppd_name = g_strconcat(VPN_STATEDIR, "/connman-ppp-option.conf", NULL);
 
        pppd_fd = open(pppd_name, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
        if (pppd_fd < 0) {
                connman_error("Error writing pppd config");
                g_free(l2tp_name);
+               g_free(ctrl_name);
                g_free(pppd_name);
                close(l2tp_fd);
                err = -EIO;
@@ -691,34 +751,28 @@ static int run_connect(struct vpn_provider *provider,
        write_pppd_option(provider, pppd_fd);
 
        connman_task_add_argument(task, "-D", NULL);
+       connman_task_add_argument(task, "-C", ctrl_name);
        connman_task_add_argument(task, "-c", l2tp_name);
 
        g_free(l2tp_name);
+       g_free(ctrl_name);
        g_free(pppd_name);
        close(l2tp_fd);
        close(pppd_fd);
 
-       err = connman_task_run(task, l2tp_died, provider,
-                               NULL, NULL, NULL);
+       err = connman_task_run(task, l2tp_died, data, NULL, NULL, NULL);
        if (err < 0) {
                connman_error("l2tp failed to start");
                err = -EIO;
-               goto done;
        }
 
 done:
-       if (cb)
-               cb(provider, user_data, err);
+       if (err)
+               l2tp_connect_done(data, -err);
 
        return err;
 }
 
-static void free_private_data(struct l2tp_private_data *data)
-{
-       g_free(data->if_name);
-       g_free(data);
-}
-
 static void request_input_cb(struct vpn_provider *provider,
                        const char *username,
                        const char *password,
@@ -736,10 +790,7 @@ static void request_input_cb(struct vpn_provider *provider,
        vpn_provider_set_string_hide_value(provider, "L2TP.Password",
                                                                password);
 
-       run_connect(provider, data->task, data->if_name, data->cb,
-               data->user_data, username, password);
-
-       free_private_data(data);
+       run_connect(data, username, password);
 }
 
 static int l2tp_connect(struct vpn_provider *provider,
@@ -747,9 +798,21 @@ static int l2tp_connect(struct vpn_provider *provider,
                        vpn_provider_connect_cb_t cb, const char *dbus_sender,
                        void *user_data)
 {
+       struct l2tp_private_data *data;
        const char *username, *password;
        int err;
 
+       data = g_try_new0(struct l2tp_private_data, 1);
+       if (!data)
+               return -ENOMEM;
+
+       data->provider = vpn_provider_ref(provider);
+       data->task = task;
+       data->if_name = g_strdup(if_name);
+       data->cb = cb;
+       data->user_data = user_data;
+       vpn_provider_set_plugin_data(provider, data);
+
        if (connman_task_set_notify(task, "getsec",
                                        l2tp_get_sec, provider) != 0) {
                err = -ENOMEM;
@@ -762,33 +825,19 @@ static int l2tp_connect(struct vpn_provider *provider,
        DBG("user %s password %p", username, password);
 
        if (!username || !*username || !password || !*password) {
-               struct l2tp_private_data *data;
-
-               data = g_try_new0(struct l2tp_private_data, 1);
-               if (!data)
-                       return -ENOMEM;
-
-               data->task = task;
-               data->if_name = g_strdup(if_name);
-               data->cb = cb;
-               data->user_data = user_data;
-
                err = request_input(provider, request_input_cb, dbus_sender,
                                                                        data);
-               if (err != -EINPROGRESS) {
-                       free_private_data(data);
-                       goto done;
-               }
+               if (err != -EINPROGRESS)
+                       goto error;
+
                return err;
        }
 
-done:
-       return run_connect(provider, task, if_name, cb, user_data,
-                                                       username, password);
+       return run_connect(data, username, password);
 
 error:
-       if (cb)
-               cb(provider, user_data, err);
+       l2tp_connect_done(data, -err);
+       free_private_data(data);
 
        return err;
 }
index fc6ceff080634905afefdad07f5f19cef32f88b1..fb63a1a303d0cbf79ef43d9d251b41c6f04bbfba 100644 (file)
@@ -110,6 +110,7 @@ struct oc_private_data {
        GIOChannel *err_ch;
        enum oc_connect_type connect_type;
        bool tried_passphrase;
+       bool group_set;
 };
 
 typedef void (*request_input_reply_cb_t) (DBusMessage *reply,
@@ -473,6 +474,7 @@ static void clear_provider_credentials(struct vpn_provider *provider,
        const char *keys[] = { "OpenConnect.PKCSPassword",
                                "OpenConnect.Username",
                                "OpenConnect.Password",
+                               "OpenConnect.SecondPassword",
                                "OpenConnect.Cookie",
                                NULL
        };
@@ -794,13 +796,46 @@ static gboolean process_auth_form(void *user_data)
 {
        struct process_form_data *form_data = user_data;
        struct oc_private_data *data = form_data->data;
+       struct oc_form_opt_select *authgroup_opt;
        struct oc_form_opt *opt;
        const char *password;
+       const char *group;
+       int i;
 
        g_mutex_lock(&form_data->mutex);
 
        DBG("");
 
+       /*
+        * Special handling for "GROUP:" field, if present.
+        * Different group selections can make other fields disappear/appear
+        */
+       if (form_data->form->authgroup_opt) {
+               group = vpn_provider_get_string(data->provider, "OpenConnect.Group");
+               authgroup_opt = form_data->form->authgroup_opt;
+
+               if (group && !data->group_set) {
+                       for (i = 0; i < authgroup_opt->nr_choices; i++) {
+                               struct oc_choice *choice = authgroup_opt->choices[i];
+
+                               if (!strcmp(group, choice->label)) {
+                                       DBG("Switching to auth group: %s", group);
+                                       openconnect_set_option_value(&authgroup_opt->form,
+                                                                       choice->name);
+                                       data->group_set = true;
+                                       form_data->status = OC_FORM_RESULT_NEWGROUP;
+                                       goto out;
+                               }
+                       }
+
+                       connman_warn("Group choice %s not present", group);
+                       data->err = -EACCES;
+                       clear_provider_credentials(data->provider, true);
+                       form_data->status = OC_FORM_RESULT_ERR;
+                       goto out;
+               }
+       }
+
        switch (data->connect_type) {
        case OC_CONNECT_USERPASS:
        case OC_CONNECT_COOKIE_WITH_USERPASS:
@@ -872,12 +907,21 @@ static gboolean process_auth_form(void *user_data)
                                                "OpenConnect.Username");
                        if (user)
                                opt->_value = strdup(user);
-               } else if (opt->type == OC_FORM_OPT_PASSWORD) {
+               } else if (opt->type == OC_FORM_OPT_PASSWORD &&
+                               g_str_has_prefix(opt->name, "password")) {
+
                        const char *pass = vpn_provider_get_string(
                                                data->provider,
                                                "OpenConnect.Password");
                        if (pass)
                                opt->_value = strdup(pass);
+               } else if (opt->type == OC_FORM_OPT_PASSWORD &&
+                               g_str_has_prefix(opt->name, "secondary_password")) {
+                       const char *pass = vpn_provider_get_string(
+                                               data->provider,
+                                               "OpenConnect.SecondPassword");
+                       if (pass)
+                               opt->_value = strdup(pass);
                }
        }
 
@@ -1201,6 +1245,7 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
        const char *vpnhost = NULL;
        const char *username = NULL;
        const char *password = NULL;
+       const char *second_password = NULL;
        const char *pkcspassword = NULL;
        const char *key;
        DBusMessageIter iter, dict;
@@ -1298,6 +1343,19 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
                        dbus_message_iter_get_basic(&value, &password);
                        vpn_provider_set_string_hide_value(data->provider,
                                        "OpenConnect.Password", password);
+               } else if (g_str_equal(key, "OpenConnect.SecondPassword")) {
+                       dbus_message_iter_next(&entry);
+                       if (dbus_message_iter_get_arg_type(&entry)
+                                                       != DBUS_TYPE_VARIANT)
+                               break;
+                       dbus_message_iter_recurse(&entry, &value);
+                       if (dbus_message_iter_get_arg_type(&value)
+                                                       != DBUS_TYPE_STRING)
+                               break;
+                       dbus_message_iter_get_basic(&value, &second_password);
+                       vpn_provider_set_string_hide_value(data->provider,
+                                       "OpenConnect.SecondPassword",
+                                       second_password);
                } else if (g_str_equal(key, "OpenConnect.PKCSPassword")) {
                        dbus_message_iter_next(&entry);
                        if (dbus_message_iter_get_arg_type(&entry)
@@ -1374,6 +1432,7 @@ static int request_input_credentials_full(
        DBusMessageIter dict;
        int err;
        void *agent;
+       bool use_second_password = false;
 
        if (!data || !cb)
                return -ESRCH;
@@ -1440,6 +1499,16 @@ static int request_input_credentials_full(
                username = vpn_provider_get_string(data->provider,
                                        "OpenConnect.Username");
                vpn_agent_append_user_info(&dict, data->provider, username);
+
+               use_second_password = vpn_provider_get_boolean(data->provider,
+                                       "OpenConnect.UseSecondPassword",
+                                       false);
+
+               if (use_second_password)
+                       request_input_append_to_dict(data->provider, &dict,
+                                       request_input_append_password,
+                                       "OpenConnect.SecondPassword");
+
                break;
        case OC_CONNECT_PUBLICKEY:
                return -EINVAL;
@@ -1520,8 +1589,10 @@ static int oc_connect(struct vpn_provider *provider,
        const char *certificate;
        const char *username;
        const char *password;
+       const char *second_password = NULL;
        const char *private_key;
        int err;
+       bool use_second_password = false;
 
        connman_info("provider %p task %p", provider, task);
 
@@ -1551,8 +1622,18 @@ static int oc_connect(struct vpn_provider *provider,
                                        "OpenConnect.Username");
                password = vpn_provider_get_string(provider,
                                        "OpenConnect.Password");
+
+               use_second_password = vpn_provider_get_boolean(provider,
+                                       "OpenConnect.UseSecondPassword",
+                                       false);
+
+               if (use_second_password)
+                       second_password = vpn_provider_get_string(provider,
+                                       "OpenConnect.SecondPassword");
+
                if (!username || !password || !g_strcmp0(username, "-") ||
-                                       !g_strcmp0(password, "-"))
+                                       !g_strcmp0(password, "-") ||
+                                       (use_second_password && !second_password))
                        goto request_input;
 
                break;
index daf66cd5ceff81e9ba00df39ac4216b5191ef507..6e288efecebc2a7fc7d5515120eb54c1f227c48a 100644 (file)
 
 static DBusConnection *connection;
 
+enum opt_type {
+       OPT_NONE = 0,
+       OPT_STRING = 1,
+       OPT_BOOL = 2,
+};
+
 struct {
-       const char *cm_opt;
-       const char *ov_opt;
-       char       has_value;
+       const char      *cm_opt;
+       const char      *ov_opt;
+       const char      *ov_opt_to_null;
+       enum opt_type   opt_type;
 } ov_options[] = {
-       { "Host", "--remote", 1 },
-       { "OpenVPN.CACert", "--ca", 1 },
-       { "OpenVPN.Cert", "--cert", 1 },
-       { "OpenVPN.Key", "--key", 1 },
-       { "OpenVPN.MTU", "--tun-mtu", 1 },
-       { "OpenVPN.NSCertType", "--ns-cert-type", 1 },
-       { "OpenVPN.Proto", "--proto", 1 },
-       { "OpenVPN.Port", "--port", 1 },
-       { "OpenVPN.AuthUserPass", "--auth-user-pass", 1 },
-       { "OpenVPN.AskPass", "--askpass", 1 },
-       { "OpenVPN.AuthNoCache", "--auth-nocache", 0 },
-       { "OpenVPN.TLSRemote", "--tls-remote", 1 },
-       { "OpenVPN.TLSAuth", NULL, 1 },
-       { "OpenVPN.TLSAuthDir", NULL, 1 },
-       { "OpenVPN.TLSCipher", "--tls-cipher", 1},
-       { "OpenVPN.Cipher", "--cipher", 1 },
-       { "OpenVPN.Auth", "--auth", 1 },
-       { "OpenVPN.CompLZO", "--comp-lzo", 0 },
-       { "OpenVPN.RemoteCertTls", "--remote-cert-tls", 1 },
-       { "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},
+       { "Host", "--remote", NULL, OPT_STRING},
+       { "OpenVPN.CACert", "--ca", NULL, OPT_STRING},
+       { "OpenVPN.Cert", "--cert", NULL, OPT_STRING},
+       { "OpenVPN.Key", "--key", NULL, OPT_STRING},
+       { "OpenVPN.MTU", "--tun-mtu", NULL, OPT_STRING},
+       { "OpenVPN.NSCertType", "--ns-cert-type", NULL, OPT_STRING},
+       { "OpenVPN.Proto", "--proto", NULL, OPT_STRING},
+       { "OpenVPN.Port", "--port", NULL, OPT_STRING},
+       /*
+        * If the AuthUserPass option is "-", provide the input  via management
+        * interface. To facilitate this set the option as NULL.
+        */
+       { "OpenVPN.AuthUserPass", "--auth-user-pass", "-", OPT_STRING},
+       { "OpenVPN.AskPass", "--askpass", NULL, OPT_STRING},
+       { "OpenVPN.AuthNoCache", "--auth-nocache", NULL, OPT_BOOL},
+       { "OpenVPN.TLSRemote", "--tls-remote", NULL, OPT_STRING},
+       { "OpenVPN.TLSAuth", NULL, NULL, OPT_NONE},
+       { "OpenVPN.TLSCipher", "--tls-cipher", NULL, OPT_STRING},
+       { "OpenVPN.TLSAuthDir", NULL, NULL, OPT_NONE},
+       { "OpenVPN.Cipher", "--cipher", NULL, OPT_STRING},
+       { "OpenVPN.Auth", "--auth", NULL, OPT_STRING},
+       /* Is set to adaptive by default if value is omitted */
+       { "OpenVPN.CompLZO", "--comp-lzo", NULL, OPT_STRING},
+       { "OpenVPN.RemoteCertTls", "--remote-cert-tls", NULL, OPT_STRING},
+       { "OpenVPN.ConfigFile", "--config", NULL, OPT_STRING},
+       { "OpenVPN.DeviceType", NULL, NULL, OPT_NONE},
+       { "OpenVPN.Verb", "--verb", NULL, OPT_STRING},
+       { "OpenVPN.Ping", "--ping", NULL, OPT_STRING},
+       { "OpenVPN.PingExit", "--ping-exit", NULL, OPT_STRING},
+       { "OpenVPN.RemapUsr1", "--remap-usr1", NULL, OPT_STRING},
 };
 
 struct ov_private_data {
@@ -355,29 +367,49 @@ static int ov_save(struct vpn_provider *provider, GKeyFile *keyfile)
 static int task_append_config_data(struct vpn_provider *provider,
                                        struct connman_task *task)
 {
-       const char *option;
        int i;
 
        for (i = 0; i < (int)ARRAY_SIZE(ov_options); i++) {
-               if (!ov_options[i].ov_opt)
-                       continue;
+               const char *ov_opt = ov_options[i].ov_opt;
+               const char *cm_opt = ov_options[i].cm_opt;
+               const char *option = NULL;
+               const char *opt_to_null;
 
-               option = vpn_provider_get_string(provider,
-                                       ov_options[i].cm_opt);
-               if (!option)
+               switch (ov_options[i].opt_type) {
+               case OPT_NONE:
                        continue;
 
-               /*
-                * If the AuthUserPass option is "-", provide the input
-                * via management interface
-                */
-               if (!strcmp(ov_options[i].cm_opt, "OpenVPN.AuthUserPass") &&
-                                               !strcmp(option, "-"))
-                       option = NULL;
+               case OPT_STRING:
+                       if (!ov_opt)
+                               continue;
+
+                       option = vpn_provider_get_string(provider, cm_opt);
+                       /*
+                        * A string option may be used alone without a value
+                        * in which case the default value is used by OpenVPN.
+                        */
+                       if (!option && !vpn_provider_setting_key_exists(
+                                                       provider, ov_opt))
+                               continue;
 
-               if (connman_task_add_argument(task,
-                               ov_options[i].ov_opt,
-                               ov_options[i].has_value ? option : NULL) < 0)
+                       opt_to_null = ov_options[i].ov_opt_to_null;
+                       if (opt_to_null && !g_strcmp0(option, opt_to_null))
+                               option = NULL;
+
+                       break;
+
+               case OPT_BOOL:
+                       if (!ov_opt)
+                               continue;
+
+                       /* Ignore the boolean toggle if option is disabled. */
+                       if (!vpn_provider_get_boolean(provider, cm_opt, false))
+                               continue;
+
+                       break;
+               }
+
+               if (connman_task_add_argument(task, ov_opt, option))
                        return -EIO;
 
        }
@@ -1099,6 +1131,15 @@ static int ov_connect(struct vpn_provider *provider,
        const char *tmpdir;
        struct ov_private_data *data;
 
+       /*
+        * Explicitly set limit of 10 for authentication errors. This defines
+        * the authentication error message limit from the server before VPN
+        * agent is instructed to clear the credentials. This is effective only
+        * after a successful connection has been made within CONNECT_OK_DIFF
+        * time. User defined value for "AuthErrorLimit" overrides this.
+        */
+       vpn_provider_set_auth_error_limit(provider, 10);
+
        data = g_try_new0(struct ov_private_data, 1);
        if (!data)
                return -ENOMEM;
index 4a704bb150eabfba850b7de57a3525039fd19e6b..7274376f3b0e298f7571fde5ea2f3f3ff50455aa 100644 (file)
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2010,2013-2014  BMW Car IT GmbH.
  *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2019-2021  Jolla Ltd.
  *
  *  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
@@ -86,12 +87,40 @@ struct {
 static DBusConnection *connection;
 
 struct pptp_private_data {
+       struct vpn_provider *provider;
        struct connman_task *task;
        char *if_name;
        vpn_provider_connect_cb_t cb;
        void *user_data;
 };
 
+static void pptp_connect_done(struct pptp_private_data *data, int err)
+{
+       vpn_provider_connect_cb_t cb;
+       void *user_data;
+
+       if (!data || !data->cb)
+               return;
+
+       /* Ensure that callback is called only once */
+       cb = data->cb;
+       user_data = data->user_data;
+       data->cb = NULL;
+       data->user_data = NULL;
+       cb(data->provider, user_data, err);
+}
+
+static void free_private_data(struct pptp_private_data *data)
+{
+       if (vpn_provider_get_plugin_data(data->provider) == data)
+               vpn_provider_set_plugin_data(data->provider, NULL);
+
+       pptp_connect_done(data, EIO);
+       vpn_provider_unref(data->provider);
+       g_free(data->if_name);
+       g_free(data);
+}
+
 static DBusMessage *pptp_get_sec(struct connman_task *task,
                                DBusMessage *msg, void *user_data)
 {
@@ -125,6 +154,9 @@ static int pptp_notify(DBusMessage *msg, struct vpn_provider *provider)
        char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
        char *ifname = NULL, *nameservers = NULL;
        struct connman_ipaddress *ipaddress = NULL;
+       struct pptp_private_data *data;
+
+       data = vpn_provider_get_plugin_data(provider);
 
        dbus_message_iter_init(msg, &iter);
 
@@ -143,11 +175,22 @@ static int pptp_notify(DBusMessage *msg, struct vpn_provider *provider)
                vpn_provider_set_string_hide_value(provider, "PPTP.Password",
                                        NULL);
 
+               pptp_connect_done(data, EACCES);
                return VPN_STATE_AUTH_FAILURE;
        }
 
-       if (strcmp(reason, "connect"))
+       if (strcmp(reason, "connect")) {
+               pptp_connect_done(data, EIO);
+
+               /*
+                * Stop the task to avoid potential looping of this state when
+                * authentication fails.
+                */
+               if (data && data->task)
+                       connman_task_stop(data->task);
+
                return VPN_STATE_DISCONNECT;
+       }
 
        dbus_message_iter_recurse(&iter, &dict);
 
@@ -217,6 +260,7 @@ static int pptp_notify(DBusMessage *msg, struct vpn_provider *provider)
        g_free(nameservers);
        connman_ipaddress_free(ipaddress);
 
+       pptp_connect_done(data, 0);
        return VPN_STATE_CONNECT;
 }
 
@@ -278,6 +322,16 @@ static void pptp_write_bool_option(struct connman_task *task,
        }
 }
 
+static void pptp_died(struct connman_task *task, int exit_code,
+                                                       void *user_data)
+{
+       struct pptp_private_data *data = user_data;
+
+       vpn_died(task, exit_code, data->provider);
+
+       free_private_data(data);
+}
+
 struct request_input_reply {
        struct vpn_provider *provider;
        vpn_provider_password_cb_t callback;
@@ -435,18 +489,18 @@ static int request_input(struct vpn_provider *provider,
        return -EINPROGRESS;
 }
 
-static int run_connect(struct vpn_provider *provider,
-                       struct connman_task *task, const char *if_name,
-                       vpn_provider_connect_cb_t cb, void *user_data,
-                       const char *username, const char *password)
+static int run_connect(struct pptp_private_data *data, const char *username,
+                                                       const char *password)
 {
+       struct vpn_provider *provider = data->provider;
+       struct connman_task *task = data->task;
        GString *pptp_opt_s;
        const char *opt_s;
        const char *host;
        char *str;
        int err, i;
 
-       if (!username || !password) {
+       if (!username || !*username || !password || !*password) {
                DBG("Cannot connect username %s password %p",
                                                username, password);
                err = -EINVAL;
@@ -498,27 +552,19 @@ static int run_connect(struct vpn_provider *provider,
        connman_task_add_argument(task, "plugin",
                                SCRIPTDIR "/libppp-plugin.so");
 
-       err = connman_task_run(task, vpn_died, provider,
-                               NULL, NULL, NULL);
+       err = connman_task_run(task, pptp_died, data, NULL, NULL, NULL);
        if (err < 0) {
                connman_error("pptp failed to start");
                err = -EIO;
-               goto done;
        }
 
 done:
-       if (cb)
-               cb(provider, user_data, err);
+       if (err)
+               pptp_connect_done(data, -err);
 
        return err;
 }
 
-static void free_private_data(struct pptp_private_data *data)
-{
-       g_free(data->if_name);
-       g_free(data);
-}
-
 static void request_input_cb(struct vpn_provider *provider,
                        const char *username,
                        const char *password,
@@ -526,7 +572,7 @@ static void request_input_cb(struct vpn_provider *provider,
 {
        struct pptp_private_data *data = user_data;
 
-       if (!username || !password)
+       if (!username || !*username || !password || !*password)
                DBG("Requesting username %s or password failed, error %s",
                        username, error);
        else if (error)
@@ -536,10 +582,7 @@ static void request_input_cb(struct vpn_provider *provider,
        vpn_provider_set_string_hide_value(provider, "PPTP.Password",
                                                                password);
 
-       run_connect(provider, data->task, data->if_name, data->cb,
-               data->user_data, username, password);
-
-       free_private_data(data);
+       run_connect(data, username, password);
 }
 
 static int pptp_connect(struct vpn_provider *provider,
@@ -547,9 +590,21 @@ static int pptp_connect(struct vpn_provider *provider,
                        vpn_provider_connect_cb_t cb, const char *dbus_sender,
                        void *user_data)
 {
+       struct pptp_private_data *data;
        const char *username, *password;
        int err;
 
+       data = g_try_new0(struct pptp_private_data, 1);
+       if (!data)
+               return -ENOMEM;
+
+       data->provider = vpn_provider_ref(provider);
+       data->task = task;
+       data->if_name = g_strdup(if_name);
+       data->cb = cb;
+       data->user_data = user_data;
+       vpn_provider_set_plugin_data(provider, data);
+
        DBG("iface %s provider %p user %p", if_name, provider, user_data);
 
        if (connman_task_set_notify(task, "getsec",
@@ -563,34 +618,20 @@ static int pptp_connect(struct vpn_provider *provider,
 
        DBG("user %s password %p", username, password);
 
-       if (!username || !password) {
-               struct pptp_private_data *data;
-
-               data = g_try_new0(struct pptp_private_data, 1);
-               if (!data)
-                       return -ENOMEM;
-
-               data->task = task;
-               data->if_name = g_strdup(if_name);
-               data->cb = cb;
-               data->user_data = user_data;
-
+       if (!username || !*username || !password || !*password) {
                err = request_input(provider, request_input_cb, dbus_sender,
                                                                        data);
-               if (err != -EINPROGRESS) {
-                       free_private_data(data);
-                       goto done;
-               }
+               if (err != -EINPROGRESS)
+                       goto error;
+
                return err;
        }
 
-done:
-       return run_connect(provider, task, if_name, cb, user_data,
-                                                       username, password);
+       return run_connect(data, username, password);
 
 error:
-       if (cb)
-               cb(provider, user_data, err);
+       pptp_connect_done(data, -err);
+       free_private_data(data);
 
        return err;
 }
index 59c805c5a5aacd3eb199b86a3529e2ff16ca52ae..cc325967537221519bb3a312d3068b27ffe14d2f 100644 (file)
@@ -3,7 +3,7 @@
  *  ConnMan VPN daemon
  *
  *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
- *  Copyright (C) 2019  Jolla Ltd. All rights reserved.
+ *  Copyright (C) 2019-2021  Jolla Ltd. 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
 #include <config.h>
 #endif
 
+/*
+ * One hour difference in seconds between connections for checking whether to
+ * treat the authentication error as a real error or as a result of rapid
+ * transport change.
+ */
+#define CONNECT_OK_DIFF                ((time_t)60*60)
+#define AUTH_ERROR_LIMIT_DEFAULT       1
+
+#define STATE_INTERVAL_DEFAULT         0
+
+#define CONNMAN_STATE_ONLINE           "online"
+#define CONNMAN_STATE_OFFLINE          "offline"
+#define CONNMAN_STATE_READY            "ready"
+#define CONNMAN_STATE_IDLE             "idle"
+
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
@@ -32,6 +47,7 @@
 #include <connman/log.h>
 #include <gweb/gresolv.h>
 #include <netdb.h>
+#include <time.h>
 
 #include "../src/connman.h"
 #include "connman/agent.h"
@@ -89,15 +105,57 @@ struct vpn_provider {
        struct connman_ipaddress *prev_ipv4_addr;
        struct connman_ipaddress *prev_ipv6_addr;
        void *plugin_data;
+       unsigned int do_connect_timeout;
        unsigned int auth_error_counter;
        unsigned int conn_error_counter;
        unsigned int signal_watch;
+       unsigned int auth_error_limit;
+       time_t previous_connect_time;
+};
+
+struct vpn_provider_connect_data {
+       DBusConnection *conn;
+       DBusMessage *msg;
+       struct vpn_provider *provider;
 };
 
+static unsigned int get_connman_state_timeout;
+
+static guint connman_signal_watch;
+static guint connman_service_watch;
+
+static bool connman_online;
+static bool state_query_completed;
+
 static void append_properties(DBusMessageIter *iter,
                                struct vpn_provider *provider);
 static int vpn_provider_save(struct vpn_provider *provider);
 
+static void get_connman_state(void);
+
+static void set_state(const char *new_state)
+{
+       if (!new_state || !*new_state)
+               return;
+
+       DBG("old state %s new state %s",
+                               connman_online ?
+                               CONNMAN_STATE_ONLINE "/" CONNMAN_STATE_READY :
+                               CONNMAN_STATE_OFFLINE "/" CONNMAN_STATE_IDLE,
+                               new_state);
+
+       /* States "online" and "ready" mean connman is online */
+       if (!g_ascii_strcasecmp(new_state, CONNMAN_STATE_ONLINE) ||
+                       !g_ascii_strcasecmp(new_state, CONNMAN_STATE_READY))
+               connman_online = true;
+       /* Everything else means connman is offline */
+       else
+               connman_online = false;
+
+       DBG("set state %s connman_online=%s ", new_state,
+                               connman_online ? "true" : "false");
+}
+
 static void free_route(gpointer data)
 {
        struct vpn_route *route = data;
@@ -740,6 +798,61 @@ static DBusMessage *clear_property(DBusConnection *conn, DBusMessage *msg,
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
+static gboolean do_connect_timeout_function(gpointer data)
+{
+       struct vpn_provider_connect_data *cdata = data;
+       struct vpn_provider *provider = cdata->provider;
+       int err;
+
+       DBG("");
+
+       /* Keep in main loop if connman is not online. */
+       if (!connman_online)
+               return G_SOURCE_CONTINUE;
+
+       provider->do_connect_timeout = 0;
+       err = __vpn_provider_connect(provider, cdata->msg);
+
+       if (err < 0 && err != -EINPROGRESS) {
+               g_dbus_send_message(cdata->conn,
+                               __connman_error_failed(cdata->msg, -err));
+               cdata->msg = NULL;
+       }
+
+       return G_SOURCE_REMOVE;
+}
+
+static void do_connect_timeout_free(gpointer data)
+{
+       struct vpn_provider_connect_data *cdata = data;
+
+       if (cdata->msg)
+               g_dbus_send_message(cdata->conn,
+                               __connman_error_operation_aborted(cdata->msg));
+
+       dbus_connection_unref(cdata->conn);
+       g_free(data);
+}
+
+static void do_connect_later(struct vpn_provider *provider,
+                                       DBusConnection *conn, DBusMessage *msg)
+{
+       struct vpn_provider_connect_data *cdata =
+                               g_new0(struct vpn_provider_connect_data, 1);
+
+       cdata->conn = dbus_connection_ref(conn);
+       cdata->msg = dbus_message_ref(msg);
+       cdata->provider = provider;
+
+       if (provider->do_connect_timeout)
+               g_source_remove(provider->do_connect_timeout);
+
+       provider->do_connect_timeout =
+                       g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, 1,
+                                       do_connect_timeout_function, cdata,
+                                       do_connect_timeout_free);
+}
+
 static DBusMessage *do_connect(DBusConnection *conn, DBusMessage *msg,
                                                                void *data)
 {
@@ -748,6 +861,25 @@ static DBusMessage *do_connect(DBusConnection *conn, DBusMessage *msg,
 
        DBG("conn %p provider %p", conn, provider);
 
+       if (!connman_online) {
+               if (state_query_completed) {
+                       DBG("%s not started - ConnMan not online/ready",
+                               provider->identifier);
+                       return __connman_error_failed(msg, ENOLINK);
+               }
+
+               DBG("%s start delayed - ConnMan state not queried",
+                       provider->identifier);
+               do_connect_later(provider, conn, msg);
+               return NULL;
+       }
+
+       /* Cancel delayed connection if connmand is online. */
+       if (provider->do_connect_timeout) {
+               g_source_remove(provider->do_connect_timeout);
+               provider->do_connect_timeout = 0;
+       }
+
        err = __vpn_provider_connect(provider, msg);
        if (err < 0 && err != -EINPROGRESS)
                return __connman_error_failed(msg, -err);
@@ -1117,12 +1249,15 @@ static void reset_error_counters(struct vpn_provider *provider)
        if (!provider)
                return;
 
+       DBG("provider %p", provider);
+
        provider->auth_error_counter = provider->conn_error_counter = 0;
 }
 
 static int vpn_provider_save(struct vpn_provider *provider)
 {
        GKeyFile *keyfile;
+       const char *value;
 
        DBG("provider %p immutable %s", provider,
                                        provider->immutable ? "yes" : "no");
@@ -1153,6 +1288,11 @@ static int vpn_provider_save(struct vpn_provider *provider)
        g_key_file_set_string(keyfile, provider->identifier,
                        "VPN.Domain", provider->domain);
 
+       value = vpn_provider_get_string(provider, "AuthErrorLimit");
+       if (value)
+               g_key_file_set_string(keyfile, provider->identifier,
+                                               "AuthErrorLimit", value);
+
        if (provider->user_networks) {
                gchar **networks;
                gsize network_count;
@@ -1269,6 +1409,9 @@ static void provider_destruct(struct vpn_provider *provider)
 {
        DBG("provider %p", provider);
 
+       if (provider->do_connect_timeout)
+               g_source_remove(provider->do_connect_timeout);
+
        if (provider->notify_id != 0)
                g_source_remove(provider->notify_id);
 
@@ -1658,6 +1801,18 @@ static void append_dns(DBusMessageIter *iter, void *user_data)
                append_nameservers(iter, provider->nameservers);
 }
 
+static time_t get_uptime(void)
+{
+       struct timespec t = { 0 };
+
+       if (clock_gettime(CLOCK_BOOTTIME, &t) == -1) {
+               connman_warn("clock_gettime() error %d, uptime failed", errno);
+               return 0;
+       }
+
+       return t.tv_sec;
+}
+
 static int provider_indicate_state(struct vpn_provider *provider,
                                enum vpn_provider_state state)
 {
@@ -1673,6 +1828,8 @@ static int provider_indicate_state(struct vpn_provider *provider,
        provider->state = state;
 
        if (state == VPN_PROVIDER_STATE_READY) {
+               provider->previous_connect_time = get_uptime();
+
                connman_dbus_property_changed_basic(provider->path,
                                        VPN_CONNECTION_INTERFACE, "Index",
                                        DBUS_TYPE_INT32, &provider->index);
@@ -1885,8 +2042,10 @@ void vpn_provider_add_error(struct vpn_provider *provider,
 {
        switch (error) {
        case VPN_PROVIDER_ERROR_UNKNOWN:
+               provider->previous_connect_time = 0;
                break;
        case VPN_PROVIDER_ERROR_CONNECT_FAILED:
+               provider->previous_connect_time = 0;
                ++provider->conn_error_counter;
                break;
        case VPN_PROVIDER_ERROR_LOGIN_FAILED:
@@ -1894,6 +2053,10 @@ void vpn_provider_add_error(struct vpn_provider *provider,
                ++provider->auth_error_counter;
                break;
        }
+
+       DBG("%p connect errors %d auth errors %d", provider,
+                                               provider->conn_error_counter,
+                                               provider->auth_error_counter);
 }
 
 int vpn_provider_indicate_error(struct vpn_provider *provider,
@@ -1902,6 +2065,21 @@ int vpn_provider_indicate_error(struct vpn_provider *provider,
        DBG("provider %p id %s error %d", provider, provider->identifier,
                                                                        error);
 
+       /*
+        * Ignore adding of errors when the VPN is idle or not set. Calls may
+        * happen in a case when networks are rapidly changed and the call to
+        * vpn_died() is done before executing the connect_cb() from the
+        * plugin. Then vpn.c:vpn_died() executes the plugin specific died()
+        * function which may free the plugin private data, containing also
+        * the callback which hasn't yet been called. As a result the provider
+        * might already been reset to idle state when the callback is executed
+        * resulting in unnecessary reset of the previous successful connect
+        * timer and adding of an error for already disconnected VPN.
+        */
+       if (provider->state == VPN_PROVIDER_STATE_IDLE ||
+                               provider->state == VPN_PROVIDER_STATE_UNKNOWN)
+               return 0;
+
        vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
 
        vpn_provider_add_error(provider, error);
@@ -2053,6 +2231,7 @@ static void provider_initialize(struct vpn_provider *provider)
                                        g_free, free_route);
        provider->setting_strings = g_hash_table_new_full(g_str_hash,
                                        g_str_equal, g_free, free_setting);
+       provider->auth_error_limit = AUTH_ERROR_LIMIT_DEFAULT;
 }
 
 static struct vpn_provider *vpn_provider_new(void)
@@ -2758,6 +2937,21 @@ bool vpn_provider_get_string_immutable(struct vpn_provider *provider,
        return setting->immutable;
 }
 
+bool vpn_provider_setting_key_exists(struct vpn_provider *provider,
+                                                       const char *key)
+{
+       return g_hash_table_contains(provider->setting_strings, key);
+}
+
+void vpn_provider_set_auth_error_limit(struct vpn_provider *provider,
+                                                       unsigned int limit)
+{
+       if (!provider)
+               return;
+
+       provider->auth_error_limit = limit;
+}
+
 bool __vpn_provider_check_routes(struct vpn_provider *provider)
 {
        if (!provider)
@@ -3066,9 +3260,70 @@ const char *vpn_provider_get_path(struct vpn_provider *provider)
        return provider->path;
 }
 
+/*
+ * This crude heuristic is meant to mitigate an issue with certain VPN
+ * providers that allow only one authentication per account at a time. These
+ * providers require the VPN client shuts down cleanly by sending an exit
+ * notification. In many cases this is not possible as the transport may
+ * already be gone and there is no route to the VPN server. In such case server
+ * may return authentication error to indicate that an other client is active
+ * and reserves the slot.
+ *
+ * By allowing the VPN client to try again with the following conditons the
+ * unnecessary credential resets done by VPN agent can be avoided. VPN client
+ * is allowed to retry if 1) there was a successful connection to the server
+ * within the specified CONNECT_OK_DIFF time and 2) the provider specific
+ * limit for auth errors is not reached the unnecessary credential resets in
+ * this case are avoided.
+ *
+ * This feature is controlled by the provider specific value for
+ * "AuthErrorLimit". Setting the value to 0 feature is disabled. User defined
+ * value is preferred if set, otherwise plugin default set with
+ * vpn_provider_set_auth_error_limit() is used, which defaults to
+ * AUTH_ERROR_LIMIT_DEFAULT.
+ */
+static bool ignore_authentication_errors(struct vpn_provider *provider)
+{
+       const char *val;
+       unsigned int limit;
+       time_t uptime;
+       time_t diff;
+
+       val = vpn_provider_get_string(provider, "AuthErrorLimit");
+       if (val)
+               limit = g_ascii_strtoull(val, NULL, 10);
+       else
+               limit = provider->auth_error_limit;
+
+       if (!limit || !provider->previous_connect_time) {
+               DBG("%p errors %u %s", provider, provider->auth_error_counter,
+                                       !limit ?
+                                       "disabled by 0 limit" :
+                                       "no previous ok conn");
+               return false;
+       }
+
+       uptime = get_uptime();
+       diff = uptime - provider->previous_connect_time;
+
+       DBG("%p errors %u limit %u uptime %jd time diff %jd", provider,
+                               provider->auth_error_counter, limit,
+                               (intmax_t)uptime, (intmax_t)diff);
+
+       if (diff <= CONNECT_OK_DIFF && provider->auth_error_counter <= limit) {
+               DBG("ignore auth errors");
+               return true;
+       }
+
+       return false;
+}
+
 unsigned int vpn_provider_get_authentication_errors(
                                                struct vpn_provider *provider)
 {
+       if (ignore_authentication_errors(provider))
+               return 0;
+
        return provider->auth_error_counter;
 }
 
@@ -3214,6 +3469,215 @@ static void remove_unprovisioned_providers(void)
        g_strfreev(providers);
 }
 
+static gboolean connman_property_changed(DBusConnection *conn,
+                               DBusMessage *message,
+                               void *user_data)
+{
+       DBusMessageIter iter, value;
+       const char *key;
+       const char *signature = DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_VARIANT_AS_STRING;
+
+       if (!dbus_message_has_signature(message, signature)) {
+               connman_error("vpn connman property signature \"%s\" "
+                               "does not match expected \"%s\"",
+                       dbus_message_get_signature(message),
+                       signature);
+               return TRUE;
+       }
+
+       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("key %s", key);
+
+       if (g_str_equal(key, "State")) {
+               const char *str;
+
+               if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING)
+                       return TRUE;
+
+               dbus_message_iter_get_basic(&value, &str);
+               set_state(str);
+       }
+
+       return TRUE;
+}
+
+static void get_connman_state_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = NULL;
+       DBusError error;
+       DBusMessageIter iter, array, dict, value;
+
+       const char *signature = DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_VARIANT_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
+
+       const char *key;
+       const char *str;
+
+       DBG("");
+
+       if (!dbus_pending_call_get_completed(call))
+               goto done;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply)) {
+               connman_error("%s", error.message);
+
+               /*
+                * In case of timeout re-add the state function to main
+                * event loop.
+                */
+               if (g_ascii_strcasecmp(error.name, DBUS_ERROR_TIMEOUT) == 0) {
+                       DBG("D-Bus timeout, re-add get_connman_state()");
+                       get_connman_state();
+               } else {
+                       dbus_error_free(&error);
+                       goto done;
+               }
+       }
+
+       if (!dbus_message_has_signature(reply, signature)) {
+               connman_error("vpnd signature \"%s\" does not match "
+                               "expected \"%s\"",
+                       dbus_message_get_signature(reply),
+                       signature);
+
+               goto done;
+       }
+
+       if (!dbus_message_iter_init(reply, &array))
+               goto done;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               dbus_message_iter_recurse(&dict, &iter);
+
+               dbus_message_iter_get_basic(&iter, &key);
+
+               dbus_message_iter_next(&iter);
+               dbus_message_iter_recurse(&iter, &value);
+
+               if (g_ascii_strcasecmp(key, "State") == 0 &&
+                               dbus_message_iter_get_arg_type(&value) ==
+                                               DBUS_TYPE_STRING) {
+                       dbus_message_iter_get_basic(&value, &str);
+
+                       DBG("Got initial state %s", str);
+
+                       set_state(str);
+
+                       /* No need to process further */
+                       break;
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       state_query_completed = true;
+
+done:
+       if (reply)
+               dbus_message_unref(reply);
+
+       if (call)
+               dbus_pending_call_unref(call);
+}
+
+static gboolean run_get_connman_state(gpointer user_data)
+{
+       const char *path = "/";
+       const char *method = "GetProperties";
+       gboolean rval = FALSE;
+
+       DBusMessage *msg = NULL;
+       DBusPendingCall *call = NULL;
+
+       DBG("");
+
+       msg = dbus_message_new_method_call(CONNMAN_SERVICE, path,
+                                       CONNMAN_MANAGER_INTERFACE, method);
+       if (!msg)
+               goto out;
+
+       rval = g_dbus_send_message_with_reply(connection, msg, &call, -1);
+       if (!rval) {
+               connman_error("Cannot call %s on %s", method,
+                                               CONNMAN_MANAGER_INTERFACE);
+               goto out;
+       }
+
+       if (!call) {
+               connman_error("set pending call failed");
+               rval = FALSE;
+               goto out;
+       }
+
+       if (!dbus_pending_call_set_notify(call, get_connman_state_reply,
+                                                               NULL, NULL)) {
+               connman_error("set notify to pending call failed");
+
+               if (call)
+                       dbus_pending_call_unref(call);
+
+               rval = FALSE;
+       }
+
+out:
+       if (msg)
+               dbus_message_unref(msg);
+
+       /* In case sending was success, unset timeout function id */
+       if (rval) {
+               DBG("unsetting get_connman_state_timeout id");
+               get_connman_state_timeout = 0;
+       }
+
+       /* Return FALSE in case of success to remove from main event loop */
+       return !rval;
+}
+
+static void get_connman_state(void)
+{
+       if (get_connman_state_timeout)
+               return;
+
+       get_connman_state_timeout = g_timeout_add(STATE_INTERVAL_DEFAULT,
+                                               run_get_connman_state, NULL);
+}
+
+static void connman_service_watch_connected(DBusConnection *conn,
+                               void *user_data)
+{
+       DBG("");
+
+       get_connman_state();
+}
+
+static void connman_service_watch_disconnected(DBusConnection *conn,
+                               void *user_data)
+{
+       DBG("");
+
+       set_state(CONNMAN_STATE_IDLE);
+
+       /* Set state query variable to initial state */
+       state_query_completed = false;
+}
+
 int __vpn_provider_init(void)
 {
        int err;
@@ -3233,6 +3697,20 @@ int __vpn_provider_init(void)
 
        provider_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                NULL, unregister_provider);
+
+       connman_service_watch = g_dbus_add_service_watch(connection,
+                                       CONNMAN_SERVICE,
+                                       connman_service_watch_connected,
+                                       connman_service_watch_disconnected,
+                                       NULL, NULL);
+
+       connman_signal_watch = g_dbus_add_signal_watch(connection,
+                                       CONNMAN_SERVICE, NULL,
+                                       CONNMAN_MANAGER_INTERFACE,
+                                       PROPERTY_CHANGED,
+                                       connman_property_changed,
+                                       NULL, NULL);
+
        return 0;
 }
 
@@ -3247,5 +3725,13 @@ void __vpn_provider_cleanup(void)
        g_hash_table_destroy(provider_hash);
        provider_hash = NULL;
 
+       if (get_connman_state_timeout) {
+               if (!g_source_remove(get_connman_state_timeout))
+                       connman_error("connman state timeout not removed");
+       }
+
+       g_dbus_remove_watch(connection, connman_service_watch);
+       g_dbus_remove_watch(connection, connman_signal_watch);
+
        dbus_connection_unref(connection);
 }
index f7fa85919faebd5d5b185465eb5b0004bb772602..5d1455da544f5603b650b7d441c0aaef6279d97b 100644 (file)
@@ -88,6 +88,10 @@ int vpn_provider_set_boolean(struct vpn_provider *provider, const char *key,
                                                        bool force_change);
 bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key,
                                                        bool default_value);
+bool vpn_provider_setting_key_exists(struct vpn_provider *provider,
+                                                       const char *key);
+void vpn_provider_set_auth_error_limit(struct vpn_provider *provider,
+                                                       unsigned int limit);
 
 int vpn_provider_set_state(struct vpn_provider *provider,
                                        enum vpn_provider_state state);
index 295c05ce5363683447ed7cd1ea84ce28df386c89..5a02d779a0abf6f63766a6d077bbf7fdce878359 100644 (file)
@@ -797,75 +797,71 @@ static const char *type2string(uint16_t type)
 
 static GIOChannel *channel = NULL;
 
-struct rtnl_request {
-       struct nlmsghdr hdr;
-       struct rtgenmsg msg;
-};
-#define RTNL_REQUEST_SIZE  (sizeof(struct nlmsghdr) + sizeof(struct rtgenmsg))
+#define RTNL_REQUEST_SIZE (NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(struct rtgenmsg)))
 
 static GSList *request_list = NULL;
 static guint32 request_seq = 0;
 
-static struct rtnl_request *find_request(guint32 seq)
+static struct nlmsghdr *find_request(guint32 seq)
 {
        GSList *list;
 
        for (list = request_list; list; list = list->next) {
-               struct rtnl_request *req = list->data;
+               struct nlmsghdr *hdr = list->data;
 
-               if (req->hdr.nlmsg_seq == seq)
-                       return req;
+               if (hdr->nlmsg_seq == seq)
+                       return hdr;
        }
 
        return NULL;
 }
 
-static int send_request(struct rtnl_request *req)
+static int send_request(struct nlmsghdr *hdr)
 {
        struct sockaddr_nl addr;
        int sk;
 
        debug("%s len %d type %d flags 0x%04x seq %d",
-                               type2string(req->hdr.nlmsg_type),
-                               req->hdr.nlmsg_len, req->hdr.nlmsg_type,
-                               req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+                               type2string(hdr->nlmsg_type),
+                               hdr->nlmsg_len, hdr->nlmsg_type,
+                               hdr->nlmsg_flags, hdr->nlmsg_seq);
 
        sk = g_io_channel_unix_get_fd(channel);
 
        memset(&addr, 0, sizeof(addr));
        addr.nl_family = AF_NETLINK;
 
-       return sendto(sk, req, req->hdr.nlmsg_len, 0,
+       return sendto(sk, hdr, hdr->nlmsg_len, 0,
                                (struct sockaddr *) &addr, sizeof(addr));
 }
 
-static int queue_request(struct rtnl_request *req)
+static int queue_request(struct nlmsghdr *hdr)
 {
-       request_list = g_slist_append(request_list, req);
+       request_list = g_slist_append(request_list, hdr);
 
        if (g_slist_length(request_list) > 1)
                return 0;
 
-       return send_request(req);
+       return send_request(hdr);
 }
 
 static int process_response(guint32 seq)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
 
        debug("seq %d", seq);
 
-       req = find_request(seq);
-       if (req) {
-               request_list = g_slist_remove(request_list, req);
-               g_free(req);
+       hdr = find_request(seq);
+       if (hdr) {
+               request_list = g_slist_remove(request_list, hdr);
+               g_free(hdr);
        }
 
-       req = g_slist_nth_data(request_list, 0);
-       if (!req)
+       hdr = g_slist_nth_data(request_list, 0);
+       if (!hdr)
                return 0;
 
-       return send_request(req);
+       return send_request(hdr);
 }
 
 static void rtnl_message(void *buf, size_t len)
@@ -960,62 +956,65 @@ static gboolean netlink_event(GIOChannel *chan, GIOCondition cond, gpointer data
 
 static int send_getlink(void)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
+       struct rtgenmsg *msg;
 
        debug("");
 
-       req = g_try_malloc0(RTNL_REQUEST_SIZE);
-       if (!req)
-               return -ENOMEM;
+       hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+       hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+       hdr->nlmsg_type = RTM_GETLINK;
+       hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       hdr->nlmsg_pid = 0;
+       hdr->nlmsg_seq = request_seq++;
 
-       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
-       req->hdr.nlmsg_type = RTM_GETLINK;
-       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-       req->hdr.nlmsg_pid = 0;
-       req->hdr.nlmsg_seq = request_seq++;
-       req->msg.rtgen_family = AF_INET;
+       msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+       msg->rtgen_family = AF_INET;
 
-       return queue_request(req);
+       return queue_request(hdr);
 }
 
 static int send_getaddr(void)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
+       struct rtgenmsg *msg;
 
        debug("");
 
-       req = g_try_malloc0(RTNL_REQUEST_SIZE);
-       if (!req)
-               return -ENOMEM;
+       hdr = g_malloc0(RTNL_REQUEST_SIZE);
 
-       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
-       req->hdr.nlmsg_type = RTM_GETADDR;
-       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-       req->hdr.nlmsg_pid = 0;
-       req->hdr.nlmsg_seq = request_seq++;
-       req->msg.rtgen_family = AF_INET;
+       hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+       hdr->nlmsg_type = RTM_GETADDR;
+       hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       hdr->nlmsg_pid = 0;
+       hdr->nlmsg_seq = request_seq++;
 
-       return queue_request(req);
+       msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+       msg->rtgen_family = AF_INET;
+
+       return queue_request(hdr);
 }
 
 static int send_getroute(void)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
+       struct rtgenmsg *msg;
 
        debug("");
 
-       req = g_try_malloc0(RTNL_REQUEST_SIZE);
-       if (!req)
-               return -ENOMEM;
+       hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+       hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+       hdr->nlmsg_type = RTM_GETROUTE;
+       hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       hdr->nlmsg_pid = 0;
+       hdr->nlmsg_seq = request_seq++;
 
-       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
-       req->hdr.nlmsg_type = RTM_GETROUTE;
-       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-       req->hdr.nlmsg_pid = 0;
-       req->hdr.nlmsg_seq = request_seq++;
-       req->msg.rtgen_family = AF_INET;
+       msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+       msg->rtgen_family = AF_INET;
 
-       return queue_request(req);
+       return queue_request(hdr);
 }
 
 static gboolean update_timeout_cb(gpointer user_data)
@@ -1158,14 +1157,14 @@ void __vpn_rtnl_cleanup(void)
        update_list = NULL;
 
        for (list = request_list; list; list = list->next) {
-               struct rtnl_request *req = list->data;
+               struct nlmsghdr *hdr = list->data;
 
                debug("%s len %d type %d flags 0x%04x seq %d",
-                               type2string(req->hdr.nlmsg_type),
-                               req->hdr.nlmsg_len, req->hdr.nlmsg_type,
-                               req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+                               type2string(hdr->nlmsg_type),
+                               hdr->nlmsg_len, hdr->nlmsg_type,
+                               hdr->nlmsg_flags, hdr->nlmsg_seq);
 
-               g_free(req);
+               g_free(hdr);
                list->data = NULL;
        }