mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-25 09:39:25 +01:00
station: Move remaining connect/re-connect/roam logic
This commit is contained in:
parent
0912a6afb8
commit
25f36f5644
348
src/device.c
348
src/device.c
@ -49,7 +49,6 @@
|
||||
|
||||
struct device {
|
||||
uint32_t index;
|
||||
uint8_t preauth_bssid[ETH_ALEN];
|
||||
|
||||
struct wiphy *wiphy;
|
||||
struct netdev *netdev;
|
||||
@ -62,9 +61,6 @@ struct device {
|
||||
|
||||
static uint32_t netdev_watch;
|
||||
|
||||
static void device_netdev_event(struct netdev *netdev, enum netdev_event event,
|
||||
void *user_data);
|
||||
|
||||
/* TODO: Remove when Station/Device is split */
|
||||
static bool device_is_busy(struct device *device)
|
||||
{
|
||||
@ -74,223 +70,6 @@ static bool device_is_busy(struct device *device)
|
||||
return station_is_busy(device->station);
|
||||
}
|
||||
|
||||
static void device_reassociate_cb(struct netdev *netdev,
|
||||
enum netdev_result result,
|
||||
void *user_data)
|
||||
{
|
||||
struct device *device = user_data;
|
||||
struct station *station = device->station;
|
||||
|
||||
l_debug("%d, result: %d", device->index, result);
|
||||
|
||||
if (station->state != STATION_STATE_ROAMING)
|
||||
return;
|
||||
|
||||
if (result == NETDEV_RESULT_OK) {
|
||||
station_roamed(station);
|
||||
station_enter_state(station, STATION_STATE_CONNECTED);
|
||||
} else
|
||||
station_roam_failed(station);
|
||||
}
|
||||
|
||||
static void device_fast_transition_cb(struct netdev *netdev,
|
||||
enum netdev_result result,
|
||||
void *user_data)
|
||||
{
|
||||
struct device *device = user_data;
|
||||
struct station *station = device->station;
|
||||
|
||||
l_debug("%d, result: %d", device->index, result);
|
||||
|
||||
if (station->state != STATION_STATE_ROAMING)
|
||||
return;
|
||||
|
||||
if (result == NETDEV_RESULT_OK) {
|
||||
station_roamed(station);
|
||||
station_enter_state(station, STATION_STATE_CONNECTED);
|
||||
} else
|
||||
station_roam_failed(station);
|
||||
}
|
||||
|
||||
static void device_transition_reassociate(struct device *device,
|
||||
struct scan_bss *bss,
|
||||
struct handshake_state *new_hs)
|
||||
{
|
||||
struct station *station = device->station;
|
||||
|
||||
if (netdev_reassociate(device->netdev, bss, station->connected_bss,
|
||||
new_hs, device_netdev_event,
|
||||
device_reassociate_cb, device) < 0) {
|
||||
handshake_state_free(new_hs);
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
|
||||
station->connected_bss = bss;
|
||||
station->preparing_roam = false;
|
||||
station_enter_state(station, STATION_STATE_ROAMING);
|
||||
}
|
||||
|
||||
static bool bss_match_bssid(const void *a, const void *b)
|
||||
{
|
||||
const struct scan_bss *bss = a;
|
||||
const uint8_t *bssid = b;
|
||||
|
||||
return !memcmp(bss->addr, bssid, sizeof(bss->addr));
|
||||
}
|
||||
|
||||
static void device_preauthenticate_cb(struct netdev *netdev,
|
||||
enum netdev_result result,
|
||||
const uint8_t *pmk, void *user_data)
|
||||
{
|
||||
struct device *device = user_data;
|
||||
struct station *station = device->station;
|
||||
struct network *connected = station->connected_network;
|
||||
struct scan_bss *bss;
|
||||
struct handshake_state *new_hs;
|
||||
|
||||
l_debug("%d, result: %d", device->index, result);
|
||||
|
||||
if (!station->preparing_roam || result == NETDEV_RESULT_ABORTED)
|
||||
return;
|
||||
|
||||
bss = l_queue_find(device->station->bss_list, bss_match_bssid,
|
||||
device->preauth_bssid);
|
||||
if (!bss) {
|
||||
l_error("Roam target BSS not found");
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
|
||||
new_hs = station_handshake_setup(station, connected, bss);
|
||||
if (!new_hs) {
|
||||
l_error("device_handshake_setup failed");
|
||||
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == NETDEV_RESULT_OK) {
|
||||
uint8_t pmkid[16];
|
||||
uint8_t rsne_buf[300];
|
||||
struct ie_rsn_info rsn_info;
|
||||
|
||||
handshake_state_set_pmk(new_hs, pmk, 32);
|
||||
handshake_state_set_authenticator_address(new_hs,
|
||||
device->preauth_bssid);
|
||||
handshake_state_set_supplicant_address(new_hs,
|
||||
netdev_get_address(device->netdev));
|
||||
|
||||
/*
|
||||
* Rebuild the RSNE to include the negotiated PMKID. Note
|
||||
* supplicant_ie can't be a WPA IE here, including because
|
||||
* the WPA IE doesn't have a capabilities field and
|
||||
* target_rsne->preauthentication would have been false in
|
||||
* device_transition_start.
|
||||
*/
|
||||
ie_parse_rsne_from_data(new_hs->supplicant_ie,
|
||||
new_hs->supplicant_ie[1] + 2,
|
||||
&rsn_info);
|
||||
|
||||
handshake_state_get_pmkid(new_hs, pmkid);
|
||||
|
||||
rsn_info.num_pmkids = 1;
|
||||
rsn_info.pmkids = pmkid;
|
||||
|
||||
ie_build_rsne(&rsn_info, rsne_buf);
|
||||
handshake_state_set_supplicant_rsn(new_hs, rsne_buf);
|
||||
}
|
||||
|
||||
device_transition_reassociate(device, bss, new_hs);
|
||||
}
|
||||
|
||||
void device_transition_start(struct device *device, struct scan_bss *bss)
|
||||
{
|
||||
struct station *station = device->station;
|
||||
struct handshake_state *hs = netdev_get_handshake(device->netdev);
|
||||
struct network *connected = station->connected_network;
|
||||
enum security security = network_get_security(connected);
|
||||
uint16_t mdid;
|
||||
struct handshake_state *new_hs;
|
||||
struct ie_rsn_info cur_rsne, target_rsne;
|
||||
|
||||
l_debug("%d, target %s", device->index,
|
||||
util_address_to_string(bss->addr));
|
||||
|
||||
/* Reset AP roam flag, at this point the roaming behaves the same */
|
||||
station->ap_directed_roaming = false;
|
||||
|
||||
if (hs->mde)
|
||||
ie_parse_mobility_domain_from_data(hs->mde, hs->mde[1] + 2,
|
||||
&mdid, NULL, NULL);
|
||||
|
||||
/* Can we use Fast Transition? */
|
||||
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid) {
|
||||
/*
|
||||
* There's no need to regenerate the RSNE because neither
|
||||
* the AKM nor cipher suite can change:
|
||||
*
|
||||
* 12.5.2: "If the FTO selects a pairwise cipher suite in
|
||||
* the RSNE that is different from the ones used in the
|
||||
* Initial mobility domain association, then the AP shall
|
||||
* reject the Authentication Request with status code 19
|
||||
* (i.e., Invalid Pairwise Cipher)."
|
||||
*/
|
||||
if (netdev_fast_transition(device->netdev, bss,
|
||||
device_fast_transition_cb) < 0) {
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
|
||||
station->connected_bss = bss;
|
||||
station->preparing_roam = false;
|
||||
station_enter_state(station, STATION_STATE_ROAMING);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Non-FT transition */
|
||||
|
||||
/*
|
||||
* FT not available, we can try preauthentication if available.
|
||||
* 802.11-2012 section 11.5.9.2:
|
||||
* "A STA shall not use preauthentication within the same mobility
|
||||
* domain if AKM suite type 00-0F-AC:3 or 00-0F-AC:4 is used in
|
||||
* the current association."
|
||||
*/
|
||||
if (security == SECURITY_8021X &&
|
||||
!station->roam_no_orig_ap &&
|
||||
scan_bss_get_rsn_info(station->connected_bss,
|
||||
&cur_rsne) >= 0 &&
|
||||
scan_bss_get_rsn_info(bss, &target_rsne) >= 0 &&
|
||||
cur_rsne.preauthentication &&
|
||||
target_rsne.preauthentication) {
|
||||
/*
|
||||
* Both the current and the target AP support
|
||||
* pre-authentication and we're using 8021x authentication so
|
||||
* attempt to pre-authenticate and reassociate afterwards.
|
||||
* If the pre-authentication fails or times out we simply
|
||||
* won't supply any PMKID when reassociating.
|
||||
* Remain in the preparing_roam state.
|
||||
*/
|
||||
memcpy(device->preauth_bssid, bss->addr, ETH_ALEN);
|
||||
|
||||
if (netdev_preauthenticate(device->netdev, bss,
|
||||
device_preauthenticate_cb,
|
||||
device) >= 0)
|
||||
return;
|
||||
}
|
||||
|
||||
new_hs = station_handshake_setup(station, connected, bss);
|
||||
if (!new_hs) {
|
||||
l_error("device_handshake_setup failed in reassociation");
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
|
||||
device_transition_reassociate(device, bss, new_hs);
|
||||
}
|
||||
|
||||
static void device_ap_roam_frame_event(struct netdev *netdev,
|
||||
const struct mmpdu_header *hdr,
|
||||
const void *body, size_t body_len,
|
||||
@ -302,133 +81,6 @@ static void device_ap_roam_frame_event(struct netdev *netdev,
|
||||
station_ap_directed_roam(station, hdr, body, body_len);
|
||||
}
|
||||
|
||||
static void device_connect_cb(struct netdev *netdev, enum netdev_result result,
|
||||
void *user_data)
|
||||
{
|
||||
struct device *device = user_data;
|
||||
struct station *station = device->station;
|
||||
|
||||
l_debug("%d, result: %d", device->index, result);
|
||||
|
||||
if (station->connect_pending) {
|
||||
struct l_dbus_message *reply;
|
||||
|
||||
switch (result) {
|
||||
case NETDEV_RESULT_ABORTED:
|
||||
reply = dbus_error_aborted(station->connect_pending);
|
||||
break;
|
||||
case NETDEV_RESULT_OK:
|
||||
reply = l_dbus_message_new_method_return(
|
||||
station->connect_pending);
|
||||
l_dbus_message_set_arguments(reply, "");
|
||||
break;
|
||||
default:
|
||||
reply = dbus_error_failed(station->connect_pending);
|
||||
break;
|
||||
}
|
||||
|
||||
dbus_pending_reply(&station->connect_pending, reply);
|
||||
}
|
||||
|
||||
if (result != NETDEV_RESULT_OK) {
|
||||
if (result != NETDEV_RESULT_ABORTED) {
|
||||
network_connect_failed(station->connected_network);
|
||||
station_disassociated(station);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
network_connected(station->connected_network);
|
||||
station_enter_state(station, STATION_STATE_CONNECTED);
|
||||
}
|
||||
|
||||
static void device_netdev_event(struct netdev *netdev, enum netdev_event event,
|
||||
void *user_data)
|
||||
{
|
||||
struct device *device = user_data;
|
||||
struct station *station = device->station;
|
||||
|
||||
switch (event) {
|
||||
case NETDEV_EVENT_AUTHENTICATING:
|
||||
l_debug("Authenticating");
|
||||
break;
|
||||
case NETDEV_EVENT_ASSOCIATING:
|
||||
l_debug("Associating");
|
||||
break;
|
||||
case NETDEV_EVENT_LOST_BEACON:
|
||||
station_lost_beacon(station);
|
||||
break;
|
||||
case NETDEV_EVENT_DISCONNECT_BY_AP:
|
||||
case NETDEV_EVENT_DISCONNECT_BY_SME:
|
||||
station_disconnect_event(station);
|
||||
break;
|
||||
case NETDEV_EVENT_RSSI_THRESHOLD_LOW:
|
||||
station_low_rssi(station);
|
||||
break;
|
||||
case NETDEV_EVENT_RSSI_THRESHOLD_HIGH:
|
||||
station_ok_rssi(station);
|
||||
break;
|
||||
case NETDEV_EVENT_RSSI_LEVEL_NOTIFY:
|
||||
station_rssi_level_changed(station);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
int __device_connect_network(struct device *device, struct network *network,
|
||||
struct scan_bss *bss)
|
||||
{
|
||||
struct l_dbus *dbus = dbus_get_bus();
|
||||
struct station *station = device->station;
|
||||
struct netdev *netdev = station->netdev;
|
||||
struct handshake_state *hs;
|
||||
int r;
|
||||
|
||||
if (device_is_busy(device))
|
||||
return -EBUSY;
|
||||
|
||||
hs = station_handshake_setup(station, network, bss);
|
||||
if (!hs)
|
||||
return -ENOTSUP;
|
||||
|
||||
r = netdev_connect(device->netdev, bss, hs, device_netdev_event,
|
||||
device_connect_cb, device);
|
||||
if (r < 0) {
|
||||
handshake_state_free(hs);
|
||||
return r;
|
||||
}
|
||||
|
||||
station->connected_bss = bss;
|
||||
station->connected_network = network;
|
||||
|
||||
station_enter_state(station, STATION_STATE_CONNECTING);
|
||||
|
||||
l_dbus_property_changed(dbus, netdev_get_path(netdev),
|
||||
IWD_DEVICE_INTERFACE, "ConnectedNetwork");
|
||||
l_dbus_property_changed(dbus, network_get_path(network),
|
||||
IWD_NETWORK_INTERFACE, "Connected");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void device_connect_network(struct device *device, struct network *network,
|
||||
struct scan_bss *bss,
|
||||
struct l_dbus_message *message)
|
||||
{
|
||||
struct station *station = device->station;
|
||||
int err = __device_connect_network(device, network, bss);
|
||||
|
||||
if (err < 0) {
|
||||
struct l_dbus *dbus = dbus_get_bus();
|
||||
|
||||
l_dbus_send(dbus, dbus_error_from_errno(err, message));
|
||||
return;
|
||||
}
|
||||
|
||||
station->connect_pending = l_dbus_message_ref(message);
|
||||
station->autoconnect = true;
|
||||
}
|
||||
|
||||
static struct l_dbus_message *device_scan(struct l_dbus *dbus,
|
||||
struct l_dbus_message *message,
|
||||
void *user_data)
|
||||
|
@ -22,18 +22,9 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct scan_bss;
|
||||
struct wiphy;
|
||||
struct netdev;
|
||||
struct network;
|
||||
struct device;
|
||||
|
||||
void device_transition_start(struct device *device, struct scan_bss *bss);
|
||||
|
||||
int __device_connect_network(struct device *device, struct network *network,
|
||||
struct scan_bss *bss);
|
||||
void device_connect_network(struct device *device, struct network *network,
|
||||
struct scan_bss *bss,
|
||||
struct l_dbus_message *message);
|
||||
struct device *device_create(struct wiphy *wiphy, struct netdev *netdev);
|
||||
void device_remove(struct device *device);
|
||||
|
@ -41,7 +41,6 @@
|
||||
#include "src/dbus.h"
|
||||
#include "src/agent.h"
|
||||
#include "src/netdev.h"
|
||||
#include "src/device.h"
|
||||
#include "src/wiphy.h"
|
||||
#include "src/watchlist.h"
|
||||
#include "src/station.h"
|
||||
@ -463,8 +462,6 @@ int network_autoconnect(struct network *network, struct scan_bss *bss)
|
||||
{
|
||||
struct station *station = network->station;
|
||||
struct wiphy *wiphy = station_get_wiphy(station);
|
||||
struct netdev *netdev = station_get_netdev(station);
|
||||
struct device *device = netdev_get_device(netdev);
|
||||
bool is_autoconnectable;
|
||||
bool is_rsn;
|
||||
int ret;
|
||||
@ -540,7 +537,7 @@ int network_autoconnect(struct network *network, struct scan_bss *bss)
|
||||
goto close_settings;
|
||||
}
|
||||
|
||||
return __device_connect_network(device, network, bss);
|
||||
return __station_connect_network(station, network, bss);
|
||||
|
||||
close_settings:
|
||||
network_settings_close(network);
|
||||
@ -635,8 +632,6 @@ static void passphrase_callback(enum agent_result result,
|
||||
{
|
||||
struct network *network = user_data;
|
||||
struct station *station = network->station;
|
||||
struct netdev *netdev = station_get_netdev(station);
|
||||
struct device *device = netdev_get_device(netdev);
|
||||
struct scan_bss *bss;
|
||||
|
||||
l_debug("result %d", result);
|
||||
@ -686,7 +681,7 @@ static void passphrase_callback(enum agent_result result,
|
||||
*/
|
||||
network->update_psk = true;
|
||||
|
||||
device_connect_network(device, network, bss, message);
|
||||
station_connect_network(station, network, bss, message);
|
||||
l_dbus_message_unref(message);
|
||||
return;
|
||||
|
||||
@ -712,8 +707,6 @@ static struct l_dbus_message *network_connect_psk(struct network *network,
|
||||
struct l_dbus_message *message)
|
||||
{
|
||||
struct station *station = network->station;
|
||||
struct netdev *netdev = station_get_netdev(station);
|
||||
struct device *device = netdev_get_device(netdev);
|
||||
|
||||
l_debug("");
|
||||
|
||||
@ -742,7 +735,7 @@ static struct l_dbus_message *network_connect_psk(struct network *network,
|
||||
if (!network->agent_request)
|
||||
return dbus_error_no_agent(message);
|
||||
} else
|
||||
device_connect_network(device, network, bss, message);
|
||||
station_connect_network(station, network, bss, message);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -942,8 +935,6 @@ static struct l_dbus_message *network_connect_8021x(struct network *network,
|
||||
struct l_dbus_message *message)
|
||||
{
|
||||
struct station *station = network->station;
|
||||
struct netdev *netdev = station_get_netdev(station);
|
||||
struct device *device = netdev_get_device(netdev);
|
||||
int r;
|
||||
struct l_queue *missing_secrets = NULL;
|
||||
struct l_dbus_message *reply;
|
||||
@ -976,7 +967,7 @@ static struct l_dbus_message *network_connect_8021x(struct network *network,
|
||||
goto error;
|
||||
}
|
||||
|
||||
device_connect_network(device, network, bss, message);
|
||||
station_connect_network(station, network, bss, message);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -1002,8 +993,6 @@ static struct l_dbus_message *network_connect(struct l_dbus *dbus,
|
||||
{
|
||||
struct network *network = user_data;
|
||||
struct station *station = network->station;
|
||||
struct netdev *netdev = station_get_netdev(station);
|
||||
struct device *device = netdev_get_device(netdev);
|
||||
struct scan_bss *bss;
|
||||
|
||||
l_debug("");
|
||||
@ -1026,7 +1015,7 @@ static struct l_dbus_message *network_connect(struct l_dbus *dbus,
|
||||
case SECURITY_PSK:
|
||||
return network_connect_psk(network, bss, message);
|
||||
case SECURITY_NONE:
|
||||
device_connect_network(device, network, bss, message);
|
||||
station_connect_network(station, network, bss, message);
|
||||
return NULL;
|
||||
case SECURITY_8021X:
|
||||
if (!network_settings_load(network))
|
||||
@ -1042,8 +1031,6 @@ void network_connect_new_hidden_network(struct network *network,
|
||||
struct l_dbus_message *message)
|
||||
{
|
||||
struct station *station = network->station;
|
||||
struct netdev *netdev = station_get_netdev(station);
|
||||
struct device *device = netdev_get_device(netdev);
|
||||
struct scan_bss *bss;
|
||||
struct l_dbus_message *error;
|
||||
|
||||
@ -1071,7 +1058,7 @@ void network_connect_new_hidden_network(struct network *network,
|
||||
error = network_connect_psk(network, bss, message);
|
||||
break;
|
||||
case SECURITY_NONE:
|
||||
device_connect_network(device, network, bss, message);
|
||||
station_connect_network(station, network, bss, message);
|
||||
return;
|
||||
default:
|
||||
error = dbus_error_not_supported(message);
|
||||
|
364
src/station.c
364
src/station.c
@ -396,9 +396,9 @@ static void station_handshake_event(struct handshake_state *hs,
|
||||
}
|
||||
}
|
||||
|
||||
struct handshake_state *station_handshake_setup(struct station *station,
|
||||
struct network *network,
|
||||
struct scan_bss *bss)
|
||||
static struct handshake_state *station_handshake_setup(struct station *station,
|
||||
struct network *network,
|
||||
struct scan_bss *bss)
|
||||
{
|
||||
enum security security = network_get_security(network);
|
||||
struct wiphy *wiphy = station->wiphy;
|
||||
@ -597,7 +597,8 @@ const char *station_state_to_string(enum station_state state)
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
void station_enter_state(struct station *station, enum station_state state)
|
||||
static void station_enter_state(struct station *station,
|
||||
enum station_state state)
|
||||
{
|
||||
uint32_t index = netdev_get_ifindex(station->netdev);
|
||||
struct l_dbus *dbus = dbus_get_bus();
|
||||
@ -706,7 +707,7 @@ static void station_reset_connection_state(struct station *station)
|
||||
IWD_NETWORK_INTERFACE, "Connected");
|
||||
}
|
||||
|
||||
void station_disassociated(struct station *station)
|
||||
static void station_disassociated(struct station *station)
|
||||
{
|
||||
l_debug("%u", netdev_get_ifindex(station->netdev));
|
||||
|
||||
@ -718,7 +719,7 @@ void station_disassociated(struct station *station)
|
||||
station_enter_state(station, STATION_STATE_AUTOCONNECT);
|
||||
}
|
||||
|
||||
void station_disconnect_event(struct station *station)
|
||||
static void station_disconnect_event(struct station *station)
|
||||
{
|
||||
l_debug("%u", netdev_get_ifindex(station->netdev));
|
||||
|
||||
@ -736,7 +737,7 @@ void station_disconnect_event(struct station *station)
|
||||
|
||||
static void station_roam_timeout_rearm(struct station *station, int seconds);
|
||||
|
||||
void station_roamed(struct station *station)
|
||||
static void station_roamed(struct station *station)
|
||||
{
|
||||
/*
|
||||
* New signal high/low notification should occur on the next
|
||||
@ -747,7 +748,7 @@ void station_roamed(struct station *station)
|
||||
station->roam_no_orig_ap = false;
|
||||
}
|
||||
|
||||
void station_roam_failed(struct station *station)
|
||||
static void station_roam_failed(struct station *station)
|
||||
{
|
||||
/*
|
||||
* If we're still connected to the old BSS, only clear preparing_roam
|
||||
@ -769,6 +770,221 @@ void station_roam_failed(struct station *station)
|
||||
station_roam_timeout_rearm(station, 60);
|
||||
}
|
||||
|
||||
static void station_reassociate_cb(struct netdev *netdev,
|
||||
enum netdev_result result,
|
||||
void *user_data)
|
||||
{
|
||||
struct station *station = user_data;
|
||||
|
||||
l_debug("%u, result: %d", netdev_get_ifindex(station->netdev), result);
|
||||
|
||||
if (station->state != STATION_STATE_ROAMING)
|
||||
return;
|
||||
|
||||
if (result == NETDEV_RESULT_OK) {
|
||||
station_roamed(station);
|
||||
station_enter_state(station, STATION_STATE_CONNECTED);
|
||||
} else
|
||||
station_roam_failed(station);
|
||||
}
|
||||
|
||||
static void station_fast_transition_cb(struct netdev *netdev,
|
||||
enum netdev_result result,
|
||||
void *user_data)
|
||||
{
|
||||
struct station *station = user_data;
|
||||
|
||||
l_debug("%u, result: %d", netdev_get_ifindex(station->netdev), result);
|
||||
|
||||
if (station->state != STATION_STATE_ROAMING)
|
||||
return;
|
||||
|
||||
if (result == NETDEV_RESULT_OK) {
|
||||
station_roamed(station);
|
||||
station_enter_state(station, STATION_STATE_CONNECTED);
|
||||
} else
|
||||
station_roam_failed(station);
|
||||
}
|
||||
|
||||
static void station_netdev_event(struct netdev *netdev, enum netdev_event event,
|
||||
void *user_data);
|
||||
|
||||
static void station_transition_reassociate(struct station *station,
|
||||
struct scan_bss *bss,
|
||||
struct handshake_state *new_hs)
|
||||
{
|
||||
if (netdev_reassociate(station->netdev, bss, station->connected_bss,
|
||||
new_hs, station_netdev_event,
|
||||
station_reassociate_cb, station) < 0) {
|
||||
handshake_state_free(new_hs);
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
|
||||
station->connected_bss = bss;
|
||||
station->preparing_roam = false;
|
||||
station_enter_state(station, STATION_STATE_ROAMING);
|
||||
}
|
||||
|
||||
static bool bss_match_bssid(const void *a, const void *b)
|
||||
{
|
||||
const struct scan_bss *bss = a;
|
||||
const uint8_t *bssid = b;
|
||||
|
||||
return !memcmp(bss->addr, bssid, sizeof(bss->addr));
|
||||
}
|
||||
|
||||
static void station_preauthenticate_cb(struct netdev *netdev,
|
||||
enum netdev_result result,
|
||||
const uint8_t *pmk, void *user_data)
|
||||
{
|
||||
struct station *station = user_data;
|
||||
struct network *connected = station->connected_network;
|
||||
struct scan_bss *bss;
|
||||
struct handshake_state *new_hs;
|
||||
|
||||
l_debug("%u, result: %d", netdev_get_ifindex(station->netdev), result);
|
||||
|
||||
if (!station->preparing_roam || result == NETDEV_RESULT_ABORTED)
|
||||
return;
|
||||
|
||||
bss = l_queue_find(station->bss_list, bss_match_bssid,
|
||||
station->preauth_bssid);
|
||||
if (!bss) {
|
||||
l_error("Roam target BSS not found");
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
|
||||
new_hs = station_handshake_setup(station, connected, bss);
|
||||
if (!new_hs) {
|
||||
l_error("station_handshake_setup failed");
|
||||
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == NETDEV_RESULT_OK) {
|
||||
uint8_t pmkid[16];
|
||||
uint8_t rsne_buf[300];
|
||||
struct ie_rsn_info rsn_info;
|
||||
|
||||
handshake_state_set_pmk(new_hs, pmk, 32);
|
||||
handshake_state_set_authenticator_address(new_hs,
|
||||
station->preauth_bssid);
|
||||
handshake_state_set_supplicant_address(new_hs,
|
||||
netdev_get_address(station->netdev));
|
||||
|
||||
/*
|
||||
* Rebuild the RSNE to include the negotiated PMKID. Note
|
||||
* supplicant_ie can't be a WPA IE here, including because
|
||||
* the WPA IE doesn't have a capabilities field and
|
||||
* target_rsne->preauthentication would have been false in
|
||||
* station_transition_start.
|
||||
*/
|
||||
ie_parse_rsne_from_data(new_hs->supplicant_ie,
|
||||
new_hs->supplicant_ie[1] + 2,
|
||||
&rsn_info);
|
||||
|
||||
handshake_state_get_pmkid(new_hs, pmkid);
|
||||
|
||||
rsn_info.num_pmkids = 1;
|
||||
rsn_info.pmkids = pmkid;
|
||||
|
||||
ie_build_rsne(&rsn_info, rsne_buf);
|
||||
handshake_state_set_supplicant_rsn(new_hs, rsne_buf);
|
||||
}
|
||||
|
||||
station_transition_reassociate(station, bss, new_hs);
|
||||
}
|
||||
|
||||
static void station_transition_start(struct station *station,
|
||||
struct scan_bss *bss)
|
||||
{
|
||||
struct handshake_state *hs = netdev_get_handshake(station->netdev);
|
||||
struct network *connected = station->connected_network;
|
||||
enum security security = network_get_security(connected);
|
||||
uint16_t mdid;
|
||||
struct handshake_state *new_hs;
|
||||
struct ie_rsn_info cur_rsne, target_rsne;
|
||||
|
||||
l_debug("%u, target %s", netdev_get_ifindex(station->netdev),
|
||||
util_address_to_string(bss->addr));
|
||||
|
||||
/* Reset AP roam flag, at this point the roaming behaves the same */
|
||||
station->ap_directed_roaming = false;
|
||||
|
||||
if (hs->mde)
|
||||
ie_parse_mobility_domain_from_data(hs->mde, hs->mde[1] + 2,
|
||||
&mdid, NULL, NULL);
|
||||
|
||||
/* Can we use Fast Transition? */
|
||||
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid) {
|
||||
/*
|
||||
* There's no need to regenerate the RSNE because neither
|
||||
* the AKM nor cipher suite can change:
|
||||
*
|
||||
* 12.5.2: "If the FTO selects a pairwise cipher suite in
|
||||
* the RSNE that is different from the ones used in the
|
||||
* Initial mobility domain association, then the AP shall
|
||||
* reject the Authentication Request with status code 19
|
||||
* (i.e., Invalid Pairwise Cipher)."
|
||||
*/
|
||||
if (netdev_fast_transition(station->netdev, bss,
|
||||
station_fast_transition_cb) < 0) {
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
|
||||
station->connected_bss = bss;
|
||||
station->preparing_roam = false;
|
||||
station_enter_state(station, STATION_STATE_ROAMING);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Non-FT transition */
|
||||
|
||||
/*
|
||||
* FT not available, we can try preauthentication if available.
|
||||
* 802.11-2012 section 11.5.9.2:
|
||||
* "A STA shall not use preauthentication within the same mobility
|
||||
* domain if AKM suite type 00-0F-AC:3 or 00-0F-AC:4 is used in
|
||||
* the current association."
|
||||
*/
|
||||
if (security == SECURITY_8021X &&
|
||||
!station->roam_no_orig_ap &&
|
||||
scan_bss_get_rsn_info(station->connected_bss,
|
||||
&cur_rsne) >= 0 &&
|
||||
scan_bss_get_rsn_info(bss, &target_rsne) >= 0 &&
|
||||
cur_rsne.preauthentication &&
|
||||
target_rsne.preauthentication) {
|
||||
/*
|
||||
* Both the current and the target AP support
|
||||
* pre-authentication and we're using 8021x authentication so
|
||||
* attempt to pre-authenticate and reassociate afterwards.
|
||||
* If the pre-authentication fails or times out we simply
|
||||
* won't supply any PMKID when reassociating.
|
||||
* Remain in the preparing_roam state.
|
||||
*/
|
||||
memcpy(station->preauth_bssid, bss->addr, ETH_ALEN);
|
||||
|
||||
if (netdev_preauthenticate(station->netdev, bss,
|
||||
station_preauthenticate_cb,
|
||||
station) >= 0)
|
||||
return;
|
||||
}
|
||||
|
||||
new_hs = station_handshake_setup(station, connected, bss);
|
||||
if (!new_hs) {
|
||||
l_error("station_handshake_setup failed in reassociation");
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
|
||||
station_transition_reassociate(station, bss, new_hs);
|
||||
}
|
||||
|
||||
static void station_roam_scan_triggered(int err, void *user_data)
|
||||
{
|
||||
struct station *station = user_data;
|
||||
@ -789,7 +1005,6 @@ static bool station_roam_scan_notify(uint32_t wiphy_id, uint32_t ifindex,
|
||||
void *userdata)
|
||||
{
|
||||
struct station *station = userdata;
|
||||
struct device *device = netdev_get_device(station->netdev);
|
||||
struct network *network = station->connected_network;
|
||||
struct handshake_state *hs = netdev_get_handshake(station->netdev);
|
||||
struct scan_bss *bss;
|
||||
@ -895,7 +1110,7 @@ next:
|
||||
l_queue_push_tail(station->bss_list, best_bss);
|
||||
}
|
||||
|
||||
device_transition_start(device, best_bss);
|
||||
station_transition_start(station, best_bss);
|
||||
|
||||
return true;
|
||||
|
||||
@ -1155,7 +1370,7 @@ static void station_roam_timeout_rearm(struct station *station, int seconds)
|
||||
station, NULL);
|
||||
}
|
||||
|
||||
void station_lost_beacon(struct station *station)
|
||||
static void station_lost_beacon(struct station *station)
|
||||
{
|
||||
l_debug("%d", netdev_get_ifindex(station->netdev));
|
||||
|
||||
@ -1261,7 +1476,7 @@ format_error:
|
||||
l_debug("bad AP roam frame formatting");
|
||||
}
|
||||
|
||||
void station_low_rssi(struct station *station)
|
||||
static void station_low_rssi(struct station *station)
|
||||
{
|
||||
if (station->signal_low)
|
||||
return;
|
||||
@ -1276,7 +1491,7 @@ void station_low_rssi(struct station *station)
|
||||
station_roam_timeout_rearm(station, 5);
|
||||
}
|
||||
|
||||
void station_ok_rssi(struct station *station)
|
||||
static void station_ok_rssi(struct station *station)
|
||||
{
|
||||
l_timeout_remove(station->roam_trigger_timeout);
|
||||
station->roam_trigger_timeout = NULL;
|
||||
@ -1284,6 +1499,129 @@ void station_ok_rssi(struct station *station)
|
||||
station->signal_low = false;
|
||||
}
|
||||
|
||||
static void station_netdev_event(struct netdev *netdev, enum netdev_event event,
|
||||
void *user_data)
|
||||
{
|
||||
struct station *station = user_data;
|
||||
|
||||
switch (event) {
|
||||
case NETDEV_EVENT_AUTHENTICATING:
|
||||
l_debug("Authenticating");
|
||||
break;
|
||||
case NETDEV_EVENT_ASSOCIATING:
|
||||
l_debug("Associating");
|
||||
break;
|
||||
case NETDEV_EVENT_LOST_BEACON:
|
||||
station_lost_beacon(station);
|
||||
break;
|
||||
case NETDEV_EVENT_DISCONNECT_BY_AP:
|
||||
case NETDEV_EVENT_DISCONNECT_BY_SME:
|
||||
station_disconnect_event(station);
|
||||
break;
|
||||
case NETDEV_EVENT_RSSI_THRESHOLD_LOW:
|
||||
station_low_rssi(station);
|
||||
break;
|
||||
case NETDEV_EVENT_RSSI_THRESHOLD_HIGH:
|
||||
station_ok_rssi(station);
|
||||
break;
|
||||
case NETDEV_EVENT_RSSI_LEVEL_NOTIFY:
|
||||
station_rssi_level_changed(station);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static void station_connect_cb(struct netdev *netdev, enum netdev_result result,
|
||||
void *user_data)
|
||||
{
|
||||
struct station *station = user_data;
|
||||
|
||||
l_debug("%u, result: %d", netdev_get_ifindex(station->netdev), result);
|
||||
|
||||
if (station->connect_pending) {
|
||||
struct l_dbus_message *reply;
|
||||
|
||||
switch (result) {
|
||||
case NETDEV_RESULT_ABORTED:
|
||||
reply = dbus_error_aborted(station->connect_pending);
|
||||
break;
|
||||
case NETDEV_RESULT_OK:
|
||||
reply = l_dbus_message_new_method_return(
|
||||
station->connect_pending);
|
||||
l_dbus_message_set_arguments(reply, "");
|
||||
break;
|
||||
default:
|
||||
reply = dbus_error_failed(station->connect_pending);
|
||||
break;
|
||||
}
|
||||
|
||||
dbus_pending_reply(&station->connect_pending, reply);
|
||||
}
|
||||
|
||||
if (result != NETDEV_RESULT_OK) {
|
||||
if (result != NETDEV_RESULT_ABORTED) {
|
||||
network_connect_failed(station->connected_network);
|
||||
station_disassociated(station);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
network_connected(station->connected_network);
|
||||
station_enter_state(station, STATION_STATE_CONNECTED);
|
||||
}
|
||||
|
||||
int __station_connect_network(struct station *station, struct network *network,
|
||||
struct scan_bss *bss)
|
||||
{
|
||||
struct l_dbus *dbus = dbus_get_bus();
|
||||
struct netdev *netdev = station->netdev;
|
||||
struct handshake_state *hs;
|
||||
int r;
|
||||
|
||||
if (station_is_busy(station))
|
||||
return -EBUSY;
|
||||
|
||||
hs = station_handshake_setup(station, network, bss);
|
||||
if (!hs)
|
||||
return -ENOTSUP;
|
||||
|
||||
r = netdev_connect(station->netdev, bss, hs, station_netdev_event,
|
||||
station_connect_cb, station);
|
||||
if (r < 0) {
|
||||
handshake_state_free(hs);
|
||||
return r;
|
||||
}
|
||||
|
||||
station->connected_bss = bss;
|
||||
station->connected_network = network;
|
||||
|
||||
station_enter_state(station, STATION_STATE_CONNECTING);
|
||||
|
||||
l_dbus_property_changed(dbus, netdev_get_path(netdev),
|
||||
IWD_DEVICE_INTERFACE, "ConnectedNetwork");
|
||||
l_dbus_property_changed(dbus, network_get_path(network),
|
||||
IWD_NETWORK_INTERFACE, "Connected");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void station_connect_network(struct station *station, struct network *network,
|
||||
struct scan_bss *bss,
|
||||
struct l_dbus_message *message)
|
||||
{
|
||||
int err = __station_connect_network(station, network, bss);
|
||||
|
||||
if (err < 0) {
|
||||
struct l_dbus *dbus = dbus_get_bus();
|
||||
|
||||
l_dbus_send(dbus, dbus_error_from_errno(err, message));
|
||||
return;
|
||||
}
|
||||
|
||||
station->connect_pending = l_dbus_message_ref(message);
|
||||
station->autoconnect = true;
|
||||
}
|
||||
|
||||
static void station_hidden_network_scan_triggered(int err, void *user_data)
|
||||
{
|
||||
struct station *station = user_data;
|
||||
|
@ -60,6 +60,7 @@ struct station {
|
||||
struct timespec roam_min_time;
|
||||
struct l_timeout *roam_trigger_timeout;
|
||||
uint32_t roam_scan_id;
|
||||
uint8_t preauth_bssid[6];
|
||||
|
||||
struct wiphy *wiphy;
|
||||
struct netdev *netdev;
|
||||
@ -84,12 +85,7 @@ struct network *station_network_find(struct station *station, const char *ssid,
|
||||
void station_set_scan_results(struct station *station, struct l_queue *bss_list,
|
||||
bool add_to_autoconnect);
|
||||
|
||||
struct handshake_state *station_handshake_setup(struct station *station,
|
||||
struct network *network,
|
||||
struct scan_bss *bss);
|
||||
|
||||
const char *station_state_to_string(enum station_state state);
|
||||
void station_enter_state(struct station *station, enum station_state state);
|
||||
enum station_state station_get_state(struct station *station);
|
||||
uint32_t station_add_state_watch(struct station *station,
|
||||
station_state_watch_func_t func,
|
||||
@ -99,19 +95,10 @@ bool station_remove_state_watch(struct station *station, uint32_t id);
|
||||
|
||||
bool station_set_autoconnect(struct station *station, bool autoconnect);
|
||||
|
||||
void station_roam_failed(struct station *station);
|
||||
void station_roamed(struct station *station);
|
||||
void station_lost_beacon(struct station *station);
|
||||
void station_ap_directed_roam(struct station *station,
|
||||
const struct mmpdu_header *hdr,
|
||||
const void *body, size_t body_len);
|
||||
|
||||
void station_low_rssi(struct station *station);
|
||||
void station_ok_rssi(struct station *station);
|
||||
|
||||
void station_disassociated(struct station *station);
|
||||
void station_disconnect_event(struct station *station);
|
||||
|
||||
struct l_dbus_message *station_dbus_connect_hidden_network(
|
||||
struct l_dbus *dbus,
|
||||
struct l_dbus_message *message,
|
||||
@ -126,6 +113,11 @@ struct l_dbus_message *station_dbus_scan(struct l_dbus *dbus,
|
||||
struct l_dbus_message *message,
|
||||
void *user_data);
|
||||
|
||||
int __station_connect_network(struct station *station, struct network *network,
|
||||
struct scan_bss *bss);
|
||||
void station_connect_network(struct station *station, struct network *network,
|
||||
struct scan_bss *bss,
|
||||
struct l_dbus_message *message);
|
||||
int station_disconnect(struct station *station);
|
||||
|
||||
void station_rssi_level_changed(struct station *station);
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include "src/dbus.h"
|
||||
#include "src/netdev.h"
|
||||
#include "src/watchlist.h"
|
||||
#include "src/device.h"
|
||||
#include "src/wiphy.h"
|
||||
#include "src/station.h"
|
||||
#include "src/scan.h"
|
||||
@ -114,7 +113,6 @@ static struct l_dbus_message *wsc_error_time_expired(struct l_dbus_message *msg)
|
||||
}
|
||||
static void wsc_try_credentials(struct wsc *wsc)
|
||||
{
|
||||
struct device *device = netdev_get_device(wsc->netdev);
|
||||
unsigned int i;
|
||||
struct network *network;
|
||||
struct scan_bss *bss;
|
||||
@ -138,7 +136,8 @@ static void wsc_try_credentials(struct wsc *wsc)
|
||||
!network_set_psk(network, wsc->creds[i].psk))
|
||||
continue;
|
||||
|
||||
device_connect_network(device, network, bss, wsc->pending);
|
||||
station_connect_network(wsc->station, network, bss,
|
||||
wsc->pending);
|
||||
l_dbus_message_unref(wsc->pending);
|
||||
wsc->pending = NULL;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user