[RFC] Wi-Fi Protected Setup (WPS) connection
by Jose Blanquicet
Hello everybody,
We would like to propose a RFC to change the way to manage the WPS
connections. Following we present our proposal and the reasons for
suggesting it:
Current implementation of WPS connection requires to specify the service to
which the user wants to connect, and when the connection is going to
finalize, a verification is done in *handle_wps_completion* function to
ensure that the SSID of the AP actually being connected was the one
specified by the user. This violates the WiFi Alliance specification over
WPS and the WPS Certification plan because:
1. WiFi Alliance admits to certify devices that have no mean for the
user to choose an AP to connect to, they define them as "Client devices
with only a simple display or a fixed label containing a setup password"
(Pag. 12 of Wi-Fi Simple Configuration Technical Specification Version
2.0.5)
1. The WPS protocol includes the transition from un-configured to
configured state of the AP when the first Enrollee connects, some Access
Points change their SSID during this transition, that would cause the
ConnMan to invalidate the connection. A tipical example of such Access
Point is the Atheros used in test 5.4.2 of Wi-Fi CERTIFIED™ Wi-Fi Protected
Setup™ Interoperability Test Plan – Version 2.0.15
After having performed a detailed study of the wpa_supplicant
implementation for the WPS, we propose to implement a solution with the
following general characteristics:
1. The new implementation must keep current WPS implementation for
compatibility support, but as deprecated.
2. In order to meet the specifications of Wi-Fi Alliance and based on
what the WPS D-Bus Interface of wpa_supplicant offers (Documented in
https://w1.fi/wpa_supplicant/devel/dbus.html#dbus_wps) we propose to add
two methods to the Technology D-Bus Interface: *StartWPS* and *CancelWPS*,
analogous to functions WPS.Start and WPS.Cancel provided by
wpa_supplicant. In this way there is no service specified enabling simple
devices to offer a very basic interface to the user for starting the WPS
session. Both methods would be supported only by WiFi technology.
3. On the other hand, some advanced WiFi modules expose more than one
network interface (one for P2P, one for STA, one for AP ..) that can be
active at the same time, and it is then desirable to not let connman
randomly choose on which start WPS, thus we propose also to add the
possibility to specify the interface over which either the start or cancel
action will be performed. This would not be a mandatory parameter in order
to keep the things easy for more simple systems.
Please find below the implementation proposal:
*StartWPS:*
Following the approach of wpa_supplicant, we propose to add the possibility
to choose which role to assume (enrollee, registrar), which authentication
type use (pin, pbc) and get back the pin to be used (if required), as well
as wpa_supplicant does. At this point, we can briefly define a possible
prototype of the method:
dict StartWPS(dict parameters)
Where the returned dictionary could be empty or composed by at most one
entry, which will be the just generated PIN by the wpa_supplicant, in case
it was requested. On the other hand, the function would receive a
dictionary with possible keys: “Ifname", "Role", "Type" and "Pin"; where
“Ifame” is an optional parameters covering previous defined characteristic
for complex systems. Whereas, parameters "Role", "Type" and "Pin" follow
exactly the same behavior of the table presented in
https://w1.fi/wpa_supplicant/devel/dbus.html#dbus_wps for method Start
of fi.w1.wpa_supplicant1.Interface.WPS
Interface.
When the *StartWPS* method gets called, it should verify if interface was
specified through “Ifname” entry key. If so, WPS Session will be started in
that specific interface, otherwise we suggest to look for the first
available interface with the capabilities to start a WPS Session among the
device_list of the Wi-Fi Technology. If it is found, then use it, if not
return "[service].Error.NotSupported”.
Additionally, current implementation does not allow to start a WPS Session
when the device is playing as Access Point (Tethering). On the contrary,
wpa_supplicant does. Therefore, we propose to also add this possibility to
the new implementation. It would be possible if a Service D-Bus Object is
not required for starting a WPS Session, which would be true if the new
methods are added to the Technology D-Bus Interface, as we propose.
*CancelWPS:*
This function does not require for any particular parameter. So, we propose
to only continue giving the option to specify the interface. Therefore, the
possible prototype of the method would be:
void CancelWPS(dict parameters)
Where the received dictionary could be empty or composed by at most one
single entry if the user wants to specify the interface using the key
“Ifname”. If so, the *CancelWPS* function should cancel the ongoing WPS
Session running in that specific interface. Otherwise, we suggest to do it
over all the Device of the Wi-Fi Technology (device_list).
*Going into details of implementation*:
After having studied the ConnMan architecture we think to associate the WPS
concept to the Device Infrastructure because if we want to add the
possibility of specify the interface (“Ifname”), the Device is the
corresponding represents of a real device inside the ConnMan. Therefore, we
think it is the best choice. Additionally, *StartWPS* and *CancelWPS*
should be added to the device driver:
*static* *struct* connman_device_driver wifi_ng_driver = {
.name = "*wifi*",
.type = *CONNMAN_DEVICE_TYPE_WIFI*,
...
.start_wps = wifi_start_wps,
.cancel_wps = wifi_cancel_wps,
};
It means that WiFi plugging must implements both methods wifi_start_wps and
wifi_cancel_wps. In particular, wifi_start_wps will need to receive the WPS
parameters to then pass them to the ConnMan’s supplicant handler
(gsuppicant/supplicant.c), which is the only one that interacts directly
with the wpa_supplicant.
Next, the supplicant handler will call methods Start or Cancel of the
fi.w1.wpa_supplicant1.Interface.WPS
D-Bus Interface of the wpa_supplicant, with the required parameters. In
case the method Start returns the newly generated PIN it has to be
forwarded towards the Technology to then be added as an entry in the
directory that would be returned by the *StartWPS* method. It would imply
that we have to store the request message of the *StartWPS* method in order
to reply possibly with a the PIN generated by the wpa_supplicant. To do it,
we propose to store that message in the Device structure in order to be
coherent with the association we previously proposed between the WPS and
Device.
Once the WPS Session has been initiated, it could finalize successfully or
fail. The current implementation does not provide further information about
such a events. Given that, we propose to notify the following events (Just
the most commons):
- Success: WPS provisioning has finished successfully.
- Password Authentication Fail: When pin entered in the registrar was
wrong.
- PBC-Overlap: When an enrollee detects two registrars with PBC session
active.
We think to perform those notifications using a D-Bus Signal added to the
Technology Interface, let’s call it *WPSEvent*. It would simply indicate
when one of the described event takes place. We propose to just provide a
string with the name of the event: "success", "pwd-auth-fail" or
"pbc-overlap", following the wpa_supplicant notation. All this information
is already provided by the wpa_supplicant in the event
fi.w1.wpa_supplicant1.Interface.WPS.Event, thus it just need to be
forwarded to the Technology in order to be emitted.
In order to forward WPS events, we propose to follow the implementation of
the communication between the plug-in and the supplicant handler, it means
to use the callback mechanism adding a new callback:
*static* *const* GSupplicantCallbacks callbacks = {
...
.wps_event = wps_event,
};
Thus the wifi plug-in must implement the wps_event function that will
basically just call a Technology function that will emit the proposed
signal.
What do you think about our proposal? Of course, everybody is welcome to
give feedbacks and suggestions.
Best regards,
Jose Blanquicet
4 years, 8 months
connman-vpnd does not reconnect after resume
by Vasiliy Tolstov
I'm build latest connman 1.32 and connman vpn plugin.
After lid closed my laptop suspended to ram. After resume connman
succeseful reconnect to my wifi, but vpn connection not reconnected.
Relevant logs from connman-vpn service:
May 07 23:15:59 acer.yoctocloud.net connman-vpnd[8360]: wlan0 {update}
flags 36931 <UP,RUNNING>
May 07 23:15:59 acer.yoctocloud.net connman-vpnd[8360]: wlan0 {update}
flags 36867 <UP>
May 07 23:15:59 acer.yoctocloud.net connman-vpnd[8360]: wlan0
{newlink} index 3 address B8:86:87:C7:CA:5D mtu 1500
May 07 23:15:59 acer.yoctocloud.net connman-vpnd[8360]: wlan0
{newlink} index 3 operstate 2 <DOWN>
May 07 23:15:59 acer.yoctocloud.net openvpn[8363]:
[intra.office.clodo.ru] Inactivity timeout (--ping-restart),
restarting
May 07 23:15:59 acer.yoctocloud.net openvpn[8363]:
SIGUSR1[soft,ping-restart] received, process restarting
May 07 23:15:59 acer.yoctocloud.net connman-vpnd[8360]: pid 8363 was
not killed, retrying after 2 sec
May 07 23:15:59 acer.yoctocloud.net connman-vpnd[8360]: vpn0 {update}
flags 37009 <UP>
May 07 23:15:59 acer.yoctocloud.net connman-vpnd[8360]: vpn0 {newlink}
index 6 operstate 2 <DOWN>
May 07 23:15:59 acer.yoctocloud.net connman-vpnd[8360]: vpn0 {update}
flags 37008 <DOWN>
May 07 23:15:59 acer.yoctocloud.net connman-vpnd[8360]: vpn0 {newlink}
index 6 operstate 2 <DOWN>
May 07 23:15:59 acer.yoctocloud.net connman-vpnd[8360]: vpn0 {dellink}
index 6 operstate 2 <DOWN>
May 07 23:15:59 acer.yoctocloud.net connman-vpnd[8360]: vpn0 {remove} index 6
May 07 23:16:00 acer.yoctocloud.net connman-vpnd[8360]: wlan0 {update}
flags 102403 <UP,LOWER_UP>
May 07 23:16:00 acer.yoctocloud.net connman-vpnd[8360]: wlan0
{newlink} index 3 address B8:86:87:C7:CA:5D mtu 1500
May 07 23:16:00 acer.yoctocloud.net connman-vpnd[8360]: wlan0
{newlink} index 3 operstate 5 <DORMANT>
May 07 23:16:00 acer.yoctocloud.net connman-vpnd[8360]: wlan0 {update}
flags 102467 <UP,RUNNING,LOWER_UP>
May 07 23:16:00 acer.yoctocloud.net connman-vpnd[8360]: wlan0
{newlink} index 3 address B8:86:87:C7:CA:5D mtu 1500
May 07 23:16:00 acer.yoctocloud.net connman-vpnd[8360]: wlan0
{newlink} index 3 operstate 6 <UP>
May 07 23:19:14 acer.yoctocloud.net connman-vpnd[8360]: vpn0 {create}
index 7 type 65534 <NONE>
May 07 23:19:14 acer.yoctocloud.net connman-vpnd[8360]: vpn0 {update}
flags 4240 <DOWN>
May 07 23:19:14 acer.yoctocloud.net connman-vpnd[8360]: vpn0 {newlink}
index 7 operstate 2 <DOWN>
--
Vasiliy Tolstov,
e-mail: v.tolstov(a)yoctocloud.net
4 years, 8 months
Details about the Patch
by Natarajan, Ponniah (P.)
Hi,
Please let me know whether this patch as part of below link has been considered for release? Or any other mechanism is handled in current connman release v1.32
http://permalink.gmane.org/gmane.comp.handhelds.moblin.connman/18794
We are observing similar issue with connman v1.31, where sometimes upon power on of the device, settings of connman for wifi shows as "True", but when run the connmanctl to get the technology state shows the Powered state as "False"
Regards,
Ponniah Natarajan.
------------------------------------
Visteon Electronics, India.
Ph Off : +91-44-49477650
Mob : +91-98401-08041
------------------------------------
4 years, 9 months
[PATCH] dhcpv6: use correct dhcp renew time when valid life-time is infinity.
by Feng Wang
Based on RFC 3315, 22.6, the valid life-time is infinite when its
value is 0xffffffff. In the g_dhcpv6_client_get_timeouts, the expire
data type is time_t. If time_t is uint32, the last_request time plus
0xffffffff will wrapover so that expire time is smaller than current
time. Thus the dhcpv6 will restart immediately(dhcpv6_restart called).
---
gdhcp/client.c | 9 +++++++--
src/dhcpv6.c | 12 +++++++++---
2 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/gdhcp/client.c b/gdhcp/client.c
index 9012b38..2be3982 100644
--- a/gdhcp/client.c
+++ b/gdhcp/client.c
@@ -835,8 +835,13 @@ int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
if (started)
*started = dhcp_client->last_request;
- if (expire)
- *expire = dhcp_client->last_request + dhcp_client->expire;
+ if (expire) {
+ if (dhcp_client->expire == 0xffffffff)
+ /* RFC3315 22.6 infinite valid-lifetime */
+ *expire = 0xffffffff;
+ else
+ *expire = dhcp_client->last_request + dhcp_client->expire;
+ }
return 0;
}
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index 9e21040..abbc1bf 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -1195,7 +1195,7 @@ static int check_restart(struct connman_dhcpv6 *dhcp)
NULL, &expired);
current = time(NULL);
- if (current >= expired) {
+ if (current >= expired && expired != 0xffffffff) {
DBG("expired by %d secs", (int)(current - expired));
g_timeout_add(0, dhcpv6_restart, dhcp);
@@ -1442,8 +1442,14 @@ int __connman_dhcpv6_start_renew(struct connman_network *network,
/* RFC 3315, 22.4
* Client can choose the timeout.
*/
- T1 = (expired - started) / 2;
- T2 = (expired - started) / 10 * 8;
+ if (expired == 0xffffffff) {
+ /* RFC 3315, 22.6 infinite valid-lifetime */
+ T1 = 0xffffffff / 2;
+ T2 = 0xffffffff/ 10 * 8;
+ } else {
+ T1 = (expired - started) / 2;
+ T2 = (expired - started) / 10 * 8;
+ }
}
dhcp->callback = callback;
--
2.8.0.rc3.226.g39d4020
4 years, 9 months
[PATCH] Remove wifi service directories which are not provisioned and not autoconnect and non-favorite when connmand starts up. Add removed flag to prevent connmand from re-creating removed provision service directory.
by Feng Wang
---
src/config.c | 2 +-
src/connman.h | 2 ++
src/service.c | 33 ++++++++++++++++++++++++++++++---
3 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/src/config.c b/src/config.c
index dcbee06..88e35b1 100644
--- a/src/config.c
+++ b/src/config.c
@@ -186,7 +186,7 @@ static void unregister_service(gpointer data)
__connman_service_set_immutable(service, false);
__connman_service_set_config(service, NULL, NULL);
__connman_service_remove(service);
-
+ __connman_service_set_removed(service, true);
/*
* Ethernet or gadget service cannot be removed by
* __connman_service_remove() so reset the ipconfig
diff --git a/src/connman.h b/src/connman.h
index e849ed8..0eb475b 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -697,6 +697,8 @@ int __connman_service_set_immutable(struct connman_service *service,
bool immutable);
int __connman_service_set_ignore(struct connman_service *service,
bool ignore);
+int __connman_service_set_removed(struct connman_service *service,
+ bool removed);
void __connman_service_set_search_domains(struct connman_service *service,
char **domains);
diff --git a/src/service.c b/src/service.c
index 768426b..8231f4f 100644
--- a/src/service.c
+++ b/src/service.c
@@ -125,6 +125,7 @@ struct connman_service {
bool hidden_service;
char *config_file;
char *config_entry;
+ bool removed;
};
static bool allow_property_changed(struct connman_service *service);
@@ -4608,6 +4609,7 @@ static void service_initialize(struct connman_service *service)
service->hidden = false;
service->ignore = false;
+ service->removed = false;
service->connect_reason = CONNMAN_SERVICE_CONNECT_REASON_NONE;
@@ -5023,6 +5025,17 @@ int __connman_service_set_ignore(struct connman_service *service,
return 0;
}
+int __connman_service_set_removed(struct connman_service *service,
+ bool removed)
+{
+ if (!service)
+ return -EINVAL;
+
+ service->removed = removed;
+
+ return 0;
+}
+
void __connman_service_set_string(struct connman_service *service,
const char *key, const char *value)
{
@@ -6863,6 +6876,11 @@ void __connman_service_update_from_network(struct connman_network *network)
if (!service->network)
return;
+ if (service->removed) {
+ /* don't update/create removed provisioned service */
+ return;
+ }
+
name = connman_network_get_string(service->network, "Name");
if (g_strcmp0(service->name, name) != 0) {
g_free(service->name);
@@ -6995,8 +7013,9 @@ static void remove_unprovisioned_services(void)
{
gchar **services;
GKeyFile *keyfile, *configkeyfile;
- char *file, *section;
+ char *file, *section, *autoconnect;
int i = 0;
+ bool value;
services = connman_storage_get_services();
if (!services)
@@ -7012,9 +7031,17 @@ static void remove_unprovisioned_services(void)
file = g_key_file_get_string(keyfile, services[i],
"Config.file", NULL);
- if (!file)
+ if (!file) {
+ /* remove unprovisoned and Not autoconnect and non-favorite service directory */
+ autoconnect = g_key_file_get_string(keyfile, services[i],
+ "AutoConnect", NULL);
+ value = g_key_file_get_boolean(keyfile, services[i], "Favorite", NULL);
+ if (!autoconnect && !value) {
+ __connman_storage_remove_service(services[i]);
+ }
+ g_free(autoconnect);
goto next;
-
+ }
section = g_key_file_get_string(keyfile, services[i],
"Config.ident", NULL);
if (!section)
--
2.8.0.rc3.226.g39d4020
4 years, 9 months
Routing/Port forwarding between connman interfaces
by Tom
Hi all,
I try to find some hints about the issue without any luck, so here my
question:
connman will normally find the best way to connect a client to the
internet through concurrent technologies and will disable the other
interface addresse.
Now I would like to set my system up as a router with port forwarding.
What I want to know: Is there an easy way to let two addresse be
available (e.g. wifi and ethernet) and is it possible to set up iptable
rules on these interfaces without interfering with connman internals?
Thanks in advance - Tom
4 years, 9 months
[PATCH] wifi: No need to allocate and free memory if ifname is NULL
by Saurav Babu
g_strdup() will return NULL only when ifname is NULL. This patch checks
ifname before any memory allocation and free is done.
---
plugins/wifi.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/plugins/wifi.c b/plugins/wifi.c
index bb1cabc..09e83ca 100644
--- a/plugins/wifi.c
+++ b/plugins/wifi.c
@@ -3114,6 +3114,8 @@ static int enable_wifi_tethering(struct connman_technology *technology,
continue;
ifname = g_supplicant_interface_get_ifname(wifi->interface);
+ if (!ifname)
+ continue;
if (wifi->ap_supported == WIFI_AP_NOT_SUPPORTED) {
DBG("%s does not support AP mode (detected)", ifname);
@@ -3148,8 +3150,6 @@ static int enable_wifi_tethering(struct connman_technology *technology,
goto failed;
info->ifname = g_strdup(ifname);
- if (!info->ifname)
- goto failed;
wifi->tethering_param->technology = technology;
wifi->tethering_param->ssid = ssid_ap_init(identifier, passphrase);
--
1.9.1
4 years, 9 months
[PATCH v2] peer: Update Connected Propererty of P2P Technology
by Jose Blanquicet
Currently the property Connected is not used for P2P Technology thus it
always remains False. This patch aims to keep it updated in order to
make easier for the UI to show if P2P technology has an active P2P
connection or not.
This implementation has a limitation in cases of complex systems where
multiple P2P interfaces are present and can be connected simultaneously,
it is because the disconnection of one of those interfaces will cause
that the property becomes False even if there is another interface still
connected. But let say that such a complex systems are not very common.
---
doc/technology-api.txt | 3 +++
src/peer.c | 3 ++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/doc/technology-api.txt b/doc/technology-api.txt
index f3703c0..f22e9b2 100644
--- a/doc/technology-api.txt
+++ b/doc/technology-api.txt
@@ -63,6 +63,9 @@ Properties boolean Powered [readwrite]
If this property is True it means that at least one
service of this technology is in ready state.
+ In case of P2P technology, this property indicates
+ if the peer is fully connected to another peer.
+
string Name [readonly]
Name of this technology.
diff --git a/src/peer.c b/src/peer.c
index 8a380c9..ad4e445 100644
--- a/src/peer.c
+++ b/src/peer.c
@@ -905,6 +905,7 @@ int connman_peer_set_state(struct connman_peer *peer,
break;
case CONNMAN_PEER_STATE_READY:
reply_pending(peer, 0);
+ __connman_technology_set_connected(CONNMAN_SERVICE_TYPE_P2P, true);
break;
case CONNMAN_PEER_STATE_DISCONNECT:
if (peer->connection_master)
@@ -913,7 +914,7 @@ int connman_peer_set_state(struct connman_peer *peer,
__connman_dhcp_stop(peer->ipconfig);
peer->connection_master = false;
peer->sub_device = NULL;
-
+ __connman_technology_set_connected(CONNMAN_SERVICE_TYPE_P2P, false);
break;
case CONNMAN_PEER_STATE_FAILURE:
if (manage_peer_error(peer) == 0)
--
1.9.1
4 years, 9 months
[PATCH 1/3] network: connectable flag in network structure
by naveen@nestlabs.com
From: Naveen Singh <nasingh(a)google.com>
Adding connectable flag in the network structure and API to set and get
that flag. The set API can be used to mark a particular network connectable
so that this network is never removed or maked unavailable.
---
include/network.h | 5 +++++
src/network.c | 13 +++++++++++++
2 files changed, 18 insertions(+)
diff --git a/include/network.h b/include/network.h
index d772699..bb9647f 100644
--- a/include/network.h
+++ b/include/network.h
@@ -102,6 +102,11 @@ bool connman_network_get_connected(struct connman_network *network);
bool connman_network_get_associating(struct connman_network *network);
+bool connman_network_get_connectable(struct connman_network *network);
+
+int connman_network_set_connectable(struct connman_network *network,
+ bool connectable);
+
void connman_network_clear_hidden(void *user_data);
int connman_network_connect_hidden(struct connman_network *network,
char *identity, char* passphrase, void *user_data);
diff --git a/src/network.c b/src/network.c
index db3d2f3..2e423bc 100644
--- a/src/network.c
+++ b/src/network.c
@@ -50,6 +50,7 @@ struct connman_network {
bool available;
bool connected;
bool roaming;
+ bool connectable;
uint8_t strength;
uint16_t frequency;
char *identifier;
@@ -825,6 +826,18 @@ static gint compare_priority(gconstpointer a, gconstpointer b)
return driver2->priority - driver1->priority;
}
+int connman_network_set_connectable(struct connman_network *network,
+ bool connectable)
+{
+ network->connectable = connectable;
+ return 0;
+}
+
+bool connman_network_get_connectable(struct connman_network *network)
+{
+ return network->connectable;
+}
+
/**
* connman_network_driver_register:
* @driver: network driver definition
--
2.8.0.rc3.226.g39d4020
4 years, 9 months
[PATCH 1/2] Fast-reconnect and Band-steering support
by naveen@nestlabs.com
From: Naveen Singh <nasingh(a)google.com>
This patch contains new implementations for following:
1. Band Steering and Load balancing Support: Enterprise AP sometimes
denies association with assoc status code 17. The reason AP sends
this code, is to steer to some other AP (in a different band) or for
doing load balancing (in case this AP is heavily loaded). This will not
work with current connman as connman would take this disconnect
notification as normal disconnect and disable the network. This act
would cause wpa_s search for the next BSSID stop. The next time connect
attempt is issued from connman, the story would repeat again. The idea
of this patch is to not interfere with BSSID selection logic of wpa_s.
wpa_s was sending disconnect reason code on DBUS as a part of
PropertyChanged signal but there was no notification for assoc status
code.
In this commit id of hostapd (c7fb678f3109e62af1ef39be9b12bf8370c35bde)
wpa_s is also sending assoc status code as a part of DBUS
PropertyChanged signal. Idea here is that on a disconnect notification
from wpa_s connman would look into the assoc status code and if it is
set to 17, it would not proceed. This will let wpa_s continue with its
own BSSID selection logic.
2. Optimize DBUS call - In case network path is Not NULL (means a
profile is already plumbed in wpa_s), network_remove should only
be called if the new network is different from what is sitting in
wpa_s. The notion of new network here is SSID, security type and
passphrase. If the new network is same as the one in wpa_s, no need
to do remove, add and select. This would save 3 DBUS calls to wpa_s.
3. Fast Reconnect: On receiving a deauth with reason code 6, 7 or 4
wpa_supplicant will immediately try to connect back. Now with current
implementation of connman, a disconnect notification will cause network
to get disabled and connection procedure would stop. This also impacts
the roaming time in case disconnect is because of any other reason code.
The act of disabling network severly affects wpa_s connection state
machine as it would generate a deauth to current network when half way
the connection was done. The idea here is that we do not disable network
on disconnect and let wpa_s keep trying to find out that network. Only
when connman has another network this network would be removed and new
network would be added.
---
gsupplicant/gsupplicant.h | 2 +
gsupplicant/supplicant.c | 126 ++++++++++++++++++++++++++++++++++++++++++++--
plugins/wifi.c | 51 ++++++++++++++++---
3 files changed, 170 insertions(+), 9 deletions(-)
diff --git a/gsupplicant/gsupplicant.h b/gsupplicant/gsupplicant.h
index a2a7605..2f9806e 100644
--- a/gsupplicant/gsupplicant.h
+++ b/gsupplicant/gsupplicant.h
@@ -353,6 +353,8 @@ struct _GSupplicantCallbacks {
GSupplicantPeerState state);
void (*peer_request) (GSupplicantPeer *peer);
void (*debug) (const char *str);
+ void (*update_disconnect_reasoncode)(GSupplicantInterface *interface, int reasoncode);
+ void (*update_assoc_status_code)(GSupplicantInterface *interface, int reasoncode);
};
typedef struct _GSupplicantCallbacks GSupplicantCallbacks;
diff --git a/gsupplicant/supplicant.c b/gsupplicant/supplicant.c
index c8fbef6..32cbc14 100644
--- a/gsupplicant/supplicant.c
+++ b/gsupplicant/supplicant.c
@@ -150,6 +150,13 @@ struct _GSupplicantWpsCredentials {
char *key;
};
+struct added_network_information {
+ char * ssid;
+ GSupplicantSecurity security;
+ char * passphrase;
+ char * private_passphrase;
+};
+
struct _GSupplicantInterface {
char *path;
char *network_path;
@@ -181,6 +188,7 @@ struct _GSupplicantInterface {
GHashTable *bss_mapping;
void *data;
const char *pending_peer_path;
+ struct added_network_information network_info;
};
struct g_supplicant_bss {
@@ -387,6 +395,63 @@ static GSupplicantState string2state(const char *state)
return G_SUPPLICANT_STATE_UNKNOWN;
}
+static bool compare_network_parameters(GSupplicantInterface *interface, GSupplicantSSID *ssid)
+{
+ if (memcmp(interface->network_info.ssid, ssid->ssid, ssid->ssid_len))
+ return FALSE;
+
+ if (interface->network_info.security != ssid->security)
+ return FALSE;
+
+ if (interface->network_info.passphrase &&
+ g_strcmp0(interface->network_info.passphrase, ssid->passphrase) != 0) {
+ return FALSE;
+ }
+
+ if (interface->network_info.private_passphrase &&
+ g_strcmp0(interface->network_info.private_passphrase, ssid->private_key_passphrase) != 0) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void remove_network_information(GSupplicantInterface * interface)
+{
+ g_free(interface->network_info.ssid);
+ g_free(interface->network_info.passphrase);
+ g_free(interface->network_info.private_passphrase);
+ interface->network_info.ssid = NULL;
+ interface->network_info.passphrase = NULL;
+ interface->network_info.private_passphrase = NULL;
+}
+
+static int store_network_information(GSupplicantInterface * interface, GSupplicantSSID *ssid)
+{
+ interface->network_info.ssid = g_malloc(ssid->ssid_len + 1);
+ if (interface->network_info.ssid != NULL) {
+ memcpy(interface->network_info.ssid, ssid->ssid, ssid->ssid_len);
+ interface->network_info.ssid[ssid->ssid_len] = '\0';
+ } else {
+ return -ENOMEM;
+ }
+
+ interface->network_info.security = ssid->security;
+
+ if ((ssid->security == G_SUPPLICANT_SECURITY_WEP ||
+ ssid->security == G_SUPPLICANT_SECURITY_PSK ||
+ ssid->security == G_SUPPLICANT_SECURITY_NONE) &&
+ ssid->passphrase) {
+ interface->network_info.passphrase = g_strdup(ssid->passphrase);
+ }
+
+ if (ssid->security == G_SUPPLICANT_SECURITY_IEEE8021X && ssid->private_key_passphrase) {
+ interface->network_info.private_passphrase = g_strdup(ssid->private_key_passphrase);
+ }
+
+ return 0;
+}
+
static void callback_system_ready(void)
{
if (system_ready)
@@ -576,6 +641,30 @@ static void callback_peer_request(GSupplicantPeer *peer)
callbacks_pointer->peer_request(peer);
}
+static void callback_disconnect_reason_code(GSupplicantInterface *interface, int reason_code)
+{
+ if (!callbacks_pointer)
+ return;
+
+ if (!callbacks_pointer->update_disconnect_reasoncode)
+ return;
+
+ if (reason_code != 0)
+ callbacks_pointer->update_disconnect_reasoncode(interface, reason_code);
+}
+
+static void callback_assoc_status_code(GSupplicantInterface *interface, int status_code)
+{
+ if (!callbacks_pointer)
+ return;
+
+ if (!callbacks_pointer->update_assoc_status_code)
+ return;
+
+ callbacks_pointer->update_assoc_status_code(interface, status_code);
+
+}
+
static void remove_group(gpointer data)
{
GSupplicantGroup *group = data;
@@ -619,6 +708,7 @@ static void remove_interface(gpointer data)
g_free(interface->ifname);
g_free(interface->driver);
g_free(interface->bridge);
+ remove_network_information(interface);
g_free(interface);
}
@@ -2135,9 +2225,22 @@ static void interface_property(const char *key, DBusMessageIter *iter,
} else if (g_strcmp0(key, "Networks") == 0) {
supplicant_dbus_array_foreach(iter, interface_network_added,
interface);
- } else
+ } else if (g_strcmp0(key, "DisconnectReason") == 0) {
+ int reason_code;
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
+ dbus_message_iter_get_basic(iter, &reason_code);
+ callback_disconnect_reason_code(interface, reason_code);
+ }
+ } else if (g_strcmp0(key, "AssocStatusCode") == 0) {
+ int status_code;
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
+ dbus_message_iter_get_basic(iter, &status_code);
+ callback_assoc_status_code(interface, status_code);
+ }
+ } else {
SUPPLICANT_DBG("key %s type %c",
key, dbus_message_iter_get_arg_type(iter));
+ }
}
static void scan_network_update(DBusMessageIter *iter, void *user_data)
@@ -4111,6 +4214,8 @@ static void interface_add_network_result(const char *error,
interface->network_path = g_strdup(path);
+ store_network_information(interface, data->ssid);
+
supplicant_dbus_method_call(data->interface->path,
SUPPLICANT_INTERFACE ".Interface", "SelectNetwork",
interface_select_network_params,
@@ -4708,6 +4813,19 @@ int g_supplicant_interface_connect(GSupplicantInterface *interface,
g_free(data->path);
dbus_free(data);
+ /*
+ * If this add network is for the same network for which
+ * wpa_supplicant already has a profile then do not need
+ * to add another profile. Only if the profile that needs
+ * to get added is different from what is there in wpa_s
+ * delete the current one. A network is identified by its
+ * SSID, security_type and passphrase (private passphrase
+ * in case security type is 802.11x).
+ */
+ if (compare_network_parameters(interface, ssid)) {
+ return -EALREADY;
+ }
+
intf_data = dbus_malloc0(sizeof(*intf_data));
if (!intf_data)
return -ENOMEM;
@@ -4753,8 +4871,10 @@ static void network_remove_result(const char *error,
result = -ECONNABORTED;
}
- g_free(data->interface->network_path);
- data->interface->network_path = NULL;
+ g_free(data->interface->network_path);
+ data->interface->network_path = NULL;
+
+ remove_network_information(data->interface);
if (data->network_remove_in_progress == TRUE) {
data->network_remove_in_progress = FALSE;
diff --git a/plugins/wifi.c b/plugins/wifi.c
index bb1cabc..e76423d 100644
--- a/plugins/wifi.c
+++ b/plugins/wifi.c
@@ -71,6 +71,8 @@
#define P2P_LISTEN_PERIOD 500
#define P2P_LISTEN_INTERVAL 2000
+#define GSUP_80211_ASSOC_STATUS_NO_ADDITIONAL_CLIENT 17
+#define WPA_SUP_LOAD_SHAPING_MAX_RETRIES 3
static struct connman_technology *wifi_technology = NULL;
static struct connman_technology *p2p_technology = NULL;
@@ -128,6 +130,7 @@ struct wifi_data {
unsigned flags;
unsigned int watch;
int retries;
+ int wpa_sup_load_shaping_retries;
struct hidden_params *hidden;
bool postpone_hidden;
struct wifi_tethering_info *tethering_param;
@@ -144,6 +147,8 @@ struct wifi_data {
bool p2p_connecting;
bool p2p_device;
int servicing;
+ int disconnect_reasoncode;
+ int assoc_statuscode;
};
static GList *iface_list = NULL;
@@ -2291,6 +2296,19 @@ static bool handle_wps_completion(GSupplicantInterface *interface,
return true;
}
+static bool handle_assoc_status_code(GSupplicantInterface *interface,
+ struct wifi_data *wifi)
+{
+ if (wifi->state == G_SUPPLICANT_STATE_ASSOCIATING &&
+ wifi->assoc_statuscode == GSUP_80211_ASSOC_STATUS_NO_ADDITIONAL_CLIENT &&
+ wifi->wpa_sup_load_shaping_retries < WPA_SUP_LOAD_SHAPING_MAX_RETRIES) {
+ wifi->wpa_sup_load_shaping_retries ++;
+ return TRUE;
+ }
+ wifi->wpa_sup_load_shaping_retries = 0;
+ return FALSE;
+}
+
static bool handle_4way_handshake_failure(GSupplicantInterface *interface,
struct connman_network *network,
struct wifi_data *wifi)
@@ -2382,6 +2400,10 @@ static void interface_state(GSupplicantInterface *interface)
break;
connman_network_set_connected(network, true);
+
+ wifi->disconnect_reasoncode = 0;
+ wifi->assoc_statuscode = 0;
+ wifi->wpa_sup_load_shaping_retries = 0;
break;
case G_SUPPLICANT_STATE_DISCONNECTED:
@@ -2399,6 +2421,9 @@ static void interface_state(GSupplicantInterface *interface)
if (is_idle(wifi))
break;
+ if (handle_assoc_status_code(interface, wifi))
+ break;
+
/* If previous state was 4way-handshake, then
* it's either: psk was incorrect and thus we retry
* or if we reach the maximum retries we declare the
@@ -2407,12 +2432,6 @@ static void interface_state(GSupplicantInterface *interface)
network, wifi))
break;
- /* We disable the selected network, if not then
- * wpa_supplicant will loop retrying */
- if (g_supplicant_interface_enable_selected_network(interface,
- FALSE) != 0)
- DBG("Could not disable selected network");
-
connman_network_set_connected(network, false);
connman_network_set_associating(network, false);
wifi->disconnecting = false;
@@ -2935,6 +2954,24 @@ static void debug(const char *str)
connman_debug("%s", str);
}
+static void wifi_disconnect_reasoncode(GSupplicantInterface *interface, int reasoncode)
+{
+ struct wifi_data *wifi = g_supplicant_interface_get_data(interface);
+
+ if (wifi != NULL) {
+ wifi->disconnect_reasoncode = reasoncode;
+ }
+}
+
+static void wifi_assoc_status_code(GSupplicantInterface *interface, int status_code)
+{
+ struct wifi_data *wifi = g_supplicant_interface_get_data(interface);
+
+ if (wifi != NULL) {
+ wifi->assoc_statuscode = status_code;
+ }
+}
+
static const GSupplicantCallbacks callbacks = {
.system_ready = system_ready,
.system_killed = system_killed,
@@ -2953,6 +2990,8 @@ static const GSupplicantCallbacks callbacks = {
.peer_changed = peer_changed,
.peer_request = peer_request,
.debug = debug,
+ .update_disconnect_reasoncode = wifi_disconnect_reasoncode,
+ .update_assoc_status_code = wifi_assoc_status_code,
};
--
2.8.0.rc3.226.g39d4020
4 years, 9 months