3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-11-29 05:39:24 +01:00

station: Move remaining connect/re-connect/roam logic

This commit is contained in:
Denis Kenzior 2018-09-04 23:26:54 -05:00
parent 0912a6afb8
commit 25f36f5644
6 changed files with 365 additions and 406 deletions

View File

@ -49,7 +49,6 @@
struct device { struct device {
uint32_t index; uint32_t index;
uint8_t preauth_bssid[ETH_ALEN];
struct wiphy *wiphy; struct wiphy *wiphy;
struct netdev *netdev; struct netdev *netdev;
@ -62,9 +61,6 @@ struct device {
static uint32_t netdev_watch; 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 */ /* TODO: Remove when Station/Device is split */
static bool device_is_busy(struct device *device) 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); 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, static void device_ap_roam_frame_event(struct netdev *netdev,
const struct mmpdu_header *hdr, const struct mmpdu_header *hdr,
const void *body, size_t body_len, 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); 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, static struct l_dbus_message *device_scan(struct l_dbus *dbus,
struct l_dbus_message *message, struct l_dbus_message *message,
void *user_data) void *user_data)

View File

@ -22,18 +22,9 @@
#include <stdbool.h> #include <stdbool.h>
struct scan_bss;
struct wiphy; struct wiphy;
struct netdev; struct netdev;
struct network;
struct device; 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); struct device *device_create(struct wiphy *wiphy, struct netdev *netdev);
void device_remove(struct device *device); void device_remove(struct device *device);

View File

@ -41,7 +41,6 @@
#include "src/dbus.h" #include "src/dbus.h"
#include "src/agent.h" #include "src/agent.h"
#include "src/netdev.h" #include "src/netdev.h"
#include "src/device.h"
#include "src/wiphy.h" #include "src/wiphy.h"
#include "src/watchlist.h" #include "src/watchlist.h"
#include "src/station.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 station *station = network->station;
struct wiphy *wiphy = station_get_wiphy(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_autoconnectable;
bool is_rsn; bool is_rsn;
int ret; int ret;
@ -540,7 +537,7 @@ int network_autoconnect(struct network *network, struct scan_bss *bss)
goto close_settings; goto close_settings;
} }
return __device_connect_network(device, network, bss); return __station_connect_network(station, network, bss);
close_settings: close_settings:
network_settings_close(network); network_settings_close(network);
@ -635,8 +632,6 @@ static void passphrase_callback(enum agent_result result,
{ {
struct network *network = user_data; struct network *network = user_data;
struct station *station = network->station; struct station *station = network->station;
struct netdev *netdev = station_get_netdev(station);
struct device *device = netdev_get_device(netdev);
struct scan_bss *bss; struct scan_bss *bss;
l_debug("result %d", result); l_debug("result %d", result);
@ -686,7 +681,7 @@ static void passphrase_callback(enum agent_result result,
*/ */
network->update_psk = true; network->update_psk = true;
device_connect_network(device, network, bss, message); station_connect_network(station, network, bss, message);
l_dbus_message_unref(message); l_dbus_message_unref(message);
return; return;
@ -712,8 +707,6 @@ static struct l_dbus_message *network_connect_psk(struct network *network,
struct l_dbus_message *message) struct l_dbus_message *message)
{ {
struct station *station = network->station; struct station *station = network->station;
struct netdev *netdev = station_get_netdev(station);
struct device *device = netdev_get_device(netdev);
l_debug(""); l_debug("");
@ -742,7 +735,7 @@ static struct l_dbus_message *network_connect_psk(struct network *network,
if (!network->agent_request) if (!network->agent_request)
return dbus_error_no_agent(message); return dbus_error_no_agent(message);
} else } else
device_connect_network(device, network, bss, message); station_connect_network(station, network, bss, message);
return NULL; return NULL;
} }
@ -942,8 +935,6 @@ static struct l_dbus_message *network_connect_8021x(struct network *network,
struct l_dbus_message *message) struct l_dbus_message *message)
{ {
struct station *station = network->station; struct station *station = network->station;
struct netdev *netdev = station_get_netdev(station);
struct device *device = netdev_get_device(netdev);
int r; int r;
struct l_queue *missing_secrets = NULL; struct l_queue *missing_secrets = NULL;
struct l_dbus_message *reply; struct l_dbus_message *reply;
@ -976,7 +967,7 @@ static struct l_dbus_message *network_connect_8021x(struct network *network,
goto error; goto error;
} }
device_connect_network(device, network, bss, message); station_connect_network(station, network, bss, message);
return NULL; return NULL;
} }
@ -1002,8 +993,6 @@ static struct l_dbus_message *network_connect(struct l_dbus *dbus,
{ {
struct network *network = user_data; struct network *network = user_data;
struct station *station = network->station; struct station *station = network->station;
struct netdev *netdev = station_get_netdev(station);
struct device *device = netdev_get_device(netdev);
struct scan_bss *bss; struct scan_bss *bss;
l_debug(""); l_debug("");
@ -1026,7 +1015,7 @@ static struct l_dbus_message *network_connect(struct l_dbus *dbus,
case SECURITY_PSK: case SECURITY_PSK:
return network_connect_psk(network, bss, message); return network_connect_psk(network, bss, message);
case SECURITY_NONE: case SECURITY_NONE:
device_connect_network(device, network, bss, message); station_connect_network(station, network, bss, message);
return NULL; return NULL;
case SECURITY_8021X: case SECURITY_8021X:
if (!network_settings_load(network)) if (!network_settings_load(network))
@ -1042,8 +1031,6 @@ void network_connect_new_hidden_network(struct network *network,
struct l_dbus_message *message) struct l_dbus_message *message)
{ {
struct station *station = network->station; struct station *station = network->station;
struct netdev *netdev = station_get_netdev(station);
struct device *device = netdev_get_device(netdev);
struct scan_bss *bss; struct scan_bss *bss;
struct l_dbus_message *error; 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); error = network_connect_psk(network, bss, message);
break; break;
case SECURITY_NONE: case SECURITY_NONE:
device_connect_network(device, network, bss, message); station_connect_network(station, network, bss, message);
return; return;
default: default:
error = dbus_error_not_supported(message); error = dbus_error_not_supported(message);

View File

@ -396,9 +396,9 @@ static void station_handshake_event(struct handshake_state *hs,
} }
} }
struct handshake_state *station_handshake_setup(struct station *station, static struct handshake_state *station_handshake_setup(struct station *station,
struct network *network, struct network *network,
struct scan_bss *bss) struct scan_bss *bss)
{ {
enum security security = network_get_security(network); enum security security = network_get_security(network);
struct wiphy *wiphy = station->wiphy; struct wiphy *wiphy = station->wiphy;
@ -597,7 +597,8 @@ const char *station_state_to_string(enum station_state state)
return "invalid"; 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); uint32_t index = netdev_get_ifindex(station->netdev);
struct l_dbus *dbus = dbus_get_bus(); struct l_dbus *dbus = dbus_get_bus();
@ -706,7 +707,7 @@ static void station_reset_connection_state(struct station *station)
IWD_NETWORK_INTERFACE, "Connected"); 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)); 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); 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)); 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); 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 * 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; 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 * 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); 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) static void station_roam_scan_triggered(int err, void *user_data)
{ {
struct station *station = 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) void *userdata)
{ {
struct station *station = userdata; struct station *station = userdata;
struct device *device = netdev_get_device(station->netdev);
struct network *network = station->connected_network; struct network *network = station->connected_network;
struct handshake_state *hs = netdev_get_handshake(station->netdev); struct handshake_state *hs = netdev_get_handshake(station->netdev);
struct scan_bss *bss; struct scan_bss *bss;
@ -895,7 +1110,7 @@ next:
l_queue_push_tail(station->bss_list, best_bss); l_queue_push_tail(station->bss_list, best_bss);
} }
device_transition_start(device, best_bss); station_transition_start(station, best_bss);
return true; return true;
@ -1155,7 +1370,7 @@ static void station_roam_timeout_rearm(struct station *station, int seconds)
station, NULL); 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)); l_debug("%d", netdev_get_ifindex(station->netdev));
@ -1261,7 +1476,7 @@ format_error:
l_debug("bad AP roam frame formatting"); 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) if (station->signal_low)
return; return;
@ -1276,7 +1491,7 @@ void station_low_rssi(struct station *station)
station_roam_timeout_rearm(station, 5); 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); l_timeout_remove(station->roam_trigger_timeout);
station->roam_trigger_timeout = NULL; station->roam_trigger_timeout = NULL;
@ -1284,6 +1499,129 @@ void station_ok_rssi(struct station *station)
station->signal_low = false; 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) static void station_hidden_network_scan_triggered(int err, void *user_data)
{ {
struct station *station = user_data; struct station *station = user_data;

View File

@ -60,6 +60,7 @@ struct station {
struct timespec roam_min_time; struct timespec roam_min_time;
struct l_timeout *roam_trigger_timeout; struct l_timeout *roam_trigger_timeout;
uint32_t roam_scan_id; uint32_t roam_scan_id;
uint8_t preauth_bssid[6];
struct wiphy *wiphy; struct wiphy *wiphy;
struct netdev *netdev; 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, void station_set_scan_results(struct station *station, struct l_queue *bss_list,
bool add_to_autoconnect); 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); 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); enum station_state station_get_state(struct station *station);
uint32_t station_add_state_watch(struct station *station, uint32_t station_add_state_watch(struct station *station,
station_state_watch_func_t func, 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); 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, void station_ap_directed_roam(struct station *station,
const struct mmpdu_header *hdr, const struct mmpdu_header *hdr,
const void *body, size_t body_len); 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_message *station_dbus_connect_hidden_network(
struct l_dbus *dbus, struct l_dbus *dbus,
struct l_dbus_message *message, 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, struct l_dbus_message *message,
void *user_data); 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); int station_disconnect(struct station *station);
void station_rssi_level_changed(struct station *station); void station_rssi_level_changed(struct station *station);

View File

@ -31,7 +31,6 @@
#include "src/dbus.h" #include "src/dbus.h"
#include "src/netdev.h" #include "src/netdev.h"
#include "src/watchlist.h" #include "src/watchlist.h"
#include "src/device.h"
#include "src/wiphy.h" #include "src/wiphy.h"
#include "src/station.h" #include "src/station.h"
#include "src/scan.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) static void wsc_try_credentials(struct wsc *wsc)
{ {
struct device *device = netdev_get_device(wsc->netdev);
unsigned int i; unsigned int i;
struct network *network; struct network *network;
struct scan_bss *bss; struct scan_bss *bss;
@ -138,7 +136,8 @@ static void wsc_try_credentials(struct wsc *wsc)
!network_set_psk(network, wsc->creds[i].psk)) !network_set_psk(network, wsc->creds[i].psk))
continue; continue;
device_connect_network(device, network, bss, wsc->pending); station_connect_network(wsc->station, network, bss,
wsc->pending);
l_dbus_message_unref(wsc->pending); l_dbus_message_unref(wsc->pending);
wsc->pending = NULL; wsc->pending = NULL;