mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-12-22 13:02:44 +01:00
ft: netdev: station: support FT-over-DS
FT-over-DS is a way to do a Fast BSS Transition using action frames for the authenticate step. This allows a station to start a fast transition to a target AP while still being connected to the original AP. This, in theory, can result in less carrier downtime. The existing ft_sm_new was removed, and two new constructors were added; one for over-air, and another for over-ds. The internals of ft.c mostly remain the same. A flag to distinguish between air/ds was added along with a new parser to parse the action frames rather than authenticate frames. The IE parsing is identical. Netdev now just initializes the auth-proto differently depending on if its doing over-air or over-ds. A new TX authenticate function was added and used for over-ds. This will send out the IEs from ft.c with an FT Request action frame. The FT Response action frame is then recieved from the AP and fed into the auth-proto state machine. After this point ft-over-ds behaves the same as ft-over-air (associate to the target AP). Some simple code was added in station.c to determine if over-air or over-ds should be used. FT-over-DS can be beneficial in cases where the AP is directing us to roam, or if the RSSI falls below a threshold. It should not be used if we have lost communication to the AP all (beacon lost) as it only works while we can still talk to the original AP.
This commit is contained in:
parent
a432ceeee4
commit
c0c8faf32f
130
src/ft.c
130
src/ft.c
@ -151,6 +151,40 @@ static bool ft_parse_authentication_resp_frame(const uint8_t *data, size_t len,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ft_parse_action_resp_frame(const uint8_t *frame, size_t frame_len,
|
||||
const uint8_t *spa, const uint8_t *aa,
|
||||
uint16_t *out_status,
|
||||
const uint8_t **out_ies,
|
||||
size_t *out_ies_len)
|
||||
{
|
||||
uint16_t status = 0;
|
||||
|
||||
/* Category FT */
|
||||
if (frame[0] != 6)
|
||||
return false;
|
||||
|
||||
/* FT Action */
|
||||
if (frame[1] != 2)
|
||||
return false;
|
||||
|
||||
if (memcmp(frame + 2, spa, 6))
|
||||
return false;
|
||||
if (memcmp(frame + 8, aa, 6))
|
||||
return false;
|
||||
|
||||
status = l_get_le16(frame + 14);
|
||||
|
||||
if (out_status)
|
||||
*out_status = status;
|
||||
|
||||
if (status == 0 && out_ies) {
|
||||
*out_ies = frame + 16;
|
||||
*out_ies_len = frame_len - 16;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ft_parse_associate_resp_frame(const uint8_t *frame, size_t frame_len,
|
||||
uint16_t *out_status, const uint8_t **rsne,
|
||||
const uint8_t **mde, const uint8_t **fte)
|
||||
@ -289,13 +323,8 @@ error:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ft_rx_authenticate(struct auth_proto *ap, const uint8_t *frame,
|
||||
size_t frame_len)
|
||||
static int ft_process_ies(struct ft_sm *ft, const uint8_t *ies, size_t ies_len)
|
||||
{
|
||||
struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
|
||||
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
|
||||
const uint8_t *ies = NULL;
|
||||
size_t ies_len;
|
||||
struct ie_tlv_iter iter;
|
||||
const uint8_t *rsne = NULL;
|
||||
const uint8_t *mde = NULL;
|
||||
@ -303,20 +332,6 @@ static int ft_rx_authenticate(struct auth_proto *ap, const uint8_t *frame,
|
||||
struct handshake_state *hs = ft->hs;
|
||||
bool is_rsn;
|
||||
|
||||
/*
|
||||
* Parse the Authentication Response and validate the contents
|
||||
* according to 12.5.2 / 12.5.4: RSN or non-RSN Over-the-air
|
||||
* FT Protocol.
|
||||
*/
|
||||
if (!ft_parse_authentication_resp_frame(frame, frame_len,
|
||||
hs->spa, hs->aa, hs->aa, 2,
|
||||
&status_code, &ies, &ies_len))
|
||||
goto auth_error;
|
||||
|
||||
/* AP Rejected the authenticate / associate */
|
||||
if (status_code != 0)
|
||||
goto auth_error;
|
||||
|
||||
/* Check 802.11r IEs */
|
||||
if (!ies)
|
||||
goto ft_error;
|
||||
@ -449,13 +464,61 @@ static int ft_rx_authenticate(struct auth_proto *ap, const uint8_t *frame,
|
||||
|
||||
return ft_tx_reassociate(ft);
|
||||
|
||||
auth_error:
|
||||
return (int)status_code;
|
||||
|
||||
ft_error:
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
static int ft_rx_action(struct auth_proto *ap, const uint8_t *frame,
|
||||
size_t frame_len)
|
||||
{
|
||||
struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
|
||||
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
|
||||
const uint8_t *ies = NULL;
|
||||
size_t ies_len;
|
||||
|
||||
if (!ft_parse_action_resp_frame(frame, frame_len, ft->hs->spa,
|
||||
ft->hs->aa, &status_code,
|
||||
&ies, &ies_len))
|
||||
return -EBADMSG;
|
||||
|
||||
/* AP Rejected the authenticate / associate */
|
||||
if (status_code != 0)
|
||||
goto auth_error;
|
||||
|
||||
return ft_process_ies(ft, ies, ies_len);
|
||||
|
||||
auth_error:
|
||||
return (int)status_code;
|
||||
}
|
||||
|
||||
static int ft_rx_authenticate(struct auth_proto *ap, const uint8_t *frame,
|
||||
size_t frame_len)
|
||||
{
|
||||
struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
|
||||
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
|
||||
const uint8_t *ies = NULL;
|
||||
size_t ies_len;
|
||||
|
||||
/*
|
||||
* Parse the Authentication Response and validate the contents
|
||||
* according to 12.5.2 / 12.5.4: RSN or non-RSN Over-the-air
|
||||
* FT Protocol.
|
||||
*/
|
||||
if (!ft_parse_authentication_resp_frame(frame, frame_len, ft->hs->spa,
|
||||
ft->hs->aa, ft->hs->aa, 2, &status_code,
|
||||
&ies, &ies_len))
|
||||
goto auth_error;
|
||||
|
||||
/* AP Rejected the authenticate / associate */
|
||||
if (status_code != 0)
|
||||
goto auth_error;
|
||||
|
||||
return ft_process_ies(ft, ies, ies_len);
|
||||
|
||||
auth_error:
|
||||
return (int)status_code;
|
||||
}
|
||||
|
||||
static int ft_rx_associate(struct auth_proto *ap, const uint8_t *frame,
|
||||
size_t frame_len)
|
||||
{
|
||||
@ -681,9 +744,10 @@ static bool ft_start(struct auth_proto *ap)
|
||||
return true;
|
||||
}
|
||||
|
||||
struct auth_proto *ft_sm_new(struct handshake_state *hs,
|
||||
static struct auth_proto *ft_sm_new(struct handshake_state *hs,
|
||||
ft_tx_authenticate_func_t tx_auth,
|
||||
ft_tx_associate_func_t tx_assoc,
|
||||
bool over_air,
|
||||
void *user_data)
|
||||
{
|
||||
struct ft_sm *ft = l_new(struct ft_sm, 1);
|
||||
@ -693,10 +757,26 @@ struct auth_proto *ft_sm_new(struct handshake_state *hs,
|
||||
ft->hs = hs;
|
||||
ft->user_data = user_data;
|
||||
|
||||
ft->ap.rx_authenticate = ft_rx_authenticate;
|
||||
ft->ap.rx_authenticate = (over_air) ? ft_rx_authenticate : ft_rx_action;
|
||||
ft->ap.rx_associate = ft_rx_associate;
|
||||
ft->ap.start = ft_start;
|
||||
ft->ap.free = ft_sm_free;
|
||||
|
||||
return &ft->ap;
|
||||
}
|
||||
|
||||
struct auth_proto *ft_over_air_sm_new(struct handshake_state *hs,
|
||||
ft_tx_authenticate_func_t tx_auth,
|
||||
ft_tx_associate_func_t tx_assoc,
|
||||
void *user_data)
|
||||
{
|
||||
return ft_sm_new(hs, tx_auth, tx_assoc, true, user_data);
|
||||
}
|
||||
|
||||
struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
|
||||
ft_tx_authenticate_func_t tx_auth,
|
||||
ft_tx_associate_func_t tx_assoc,
|
||||
void *user_data)
|
||||
{
|
||||
return ft_sm_new(hs, tx_auth, tx_assoc, false, user_data);
|
||||
}
|
||||
|
7
src/ft.h
7
src/ft.h
@ -25,7 +25,12 @@ typedef void (*ft_tx_authenticate_func_t)(struct iovec *iov, size_t iov_len,
|
||||
typedef void (*ft_tx_associate_func_t)(struct iovec *ie_iov, size_t iov_len,
|
||||
void *user_data);
|
||||
|
||||
struct auth_proto *ft_sm_new(struct handshake_state *hs,
|
||||
struct auth_proto *ft_over_air_sm_new(struct handshake_state *hs,
|
||||
ft_tx_authenticate_func_t tx_auth,
|
||||
ft_tx_associate_func_t tx_assoc,
|
||||
void *user_data);
|
||||
|
||||
struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
|
||||
ft_tx_authenticate_func_t tx_auth,
|
||||
ft_tx_associate_func_t tx_assoc,
|
||||
void *user_data);
|
||||
|
103
src/netdev.c
103
src/netdev.c
@ -89,6 +89,7 @@ struct netdev {
|
||||
struct wiphy *wiphy;
|
||||
unsigned int ifi_flags;
|
||||
uint32_t frequency;
|
||||
uint32_t prev_frequency;
|
||||
|
||||
netdev_event_func_t event_filter;
|
||||
netdev_connect_cb_t connect_cb;
|
||||
@ -2777,7 +2778,69 @@ static void netdev_ft_tx_associate(struct iovec *ie_iov, size_t iov_len,
|
||||
}
|
||||
}
|
||||
|
||||
int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
|
||||
static void netdev_ft_request_cb(struct l_genl_msg *msg, void *user_data)
|
||||
{
|
||||
struct netdev *netdev = user_data;
|
||||
|
||||
if (l_genl_msg_get_error(msg) < 0) {
|
||||
l_error("Could not send CMD_FRAME");
|
||||
netdev_connect_failed(netdev,
|
||||
NETDEV_RESULT_AUTHENTICATION_FAILED,
|
||||
MMPDU_STATUS_CODE_UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
static void netdev_ft_response_frame_event(struct netdev *netdev,
|
||||
const struct mmpdu_header *hdr,
|
||||
const void *body, size_t body_len,
|
||||
void *user_data)
|
||||
{
|
||||
int ret;
|
||||
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
|
||||
|
||||
if (!netdev->ap || !netdev->in_ft)
|
||||
return;
|
||||
|
||||
ret = auth_proto_rx_authenticate(netdev->ap, body, body_len);
|
||||
if (ret < 0)
|
||||
goto ft_error;
|
||||
else if (ret > 0) {
|
||||
status_code = (uint16_t)ret;
|
||||
goto ft_error;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
ft_error:
|
||||
netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
|
||||
status_code);
|
||||
return;
|
||||
}
|
||||
|
||||
static void netdev_ft_over_ds_tx_authenticate(struct iovec *iov,
|
||||
size_t iov_len, void *user_data)
|
||||
{
|
||||
struct netdev *netdev = user_data;
|
||||
uint8_t ft_req[14];
|
||||
struct handshake_state *hs = netdev->handshake;
|
||||
struct iovec iovs[iov_len + 1];
|
||||
|
||||
ft_req[0] = 6; /* FT category */
|
||||
ft_req[1] = 1; /* FT Request action */
|
||||
memcpy(ft_req + 2, netdev->addr, 6);
|
||||
memcpy(ft_req + 8, hs->aa, 6);
|
||||
|
||||
iovs[0].iov_base = ft_req;
|
||||
iovs[0].iov_len = sizeof(ft_req);
|
||||
memcpy(iovs + 1, iov, sizeof(*iov) * iov_len);
|
||||
|
||||
netdev_send_action_framev(netdev, netdev->prev_bssid, iovs, iov_len + 1,
|
||||
netdev->prev_frequency,
|
||||
netdev_ft_request_cb);
|
||||
}
|
||||
|
||||
static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
|
||||
bool over_air,
|
||||
netdev_connect_cb_t cb)
|
||||
{
|
||||
struct netdev_handshake_state *nhs;
|
||||
@ -2801,17 +2864,16 @@ int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
|
||||
|
||||
handshake_state_new_snonce(netdev->handshake);
|
||||
|
||||
netdev->prev_frequency = netdev->frequency;
|
||||
netdev->frequency = target_bss->frequency;
|
||||
|
||||
handshake_state_set_authenticator_address(netdev->handshake,
|
||||
target_bss->addr);
|
||||
|
||||
handshake_state_set_authenticator_rsn(netdev->handshake,
|
||||
target_bss->rsne);
|
||||
memcpy(netdev->handshake->mde + 2, target_bss->mde, 3);
|
||||
|
||||
netdev->ap = ft_sm_new(netdev->handshake, netdev_ft_tx_authenticate,
|
||||
netdev_ft_tx_associate, netdev);
|
||||
|
||||
netdev->operational = false;
|
||||
netdev->in_ft = true;
|
||||
netdev->connect_cb = cb;
|
||||
@ -2848,14 +2910,23 @@ int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
|
||||
|
||||
netdev_rssi_polling_update(netdev);
|
||||
|
||||
if (!auth_proto_start(netdev->ap))
|
||||
goto restore_snonce;
|
||||
|
||||
if (netdev->sm) {
|
||||
eapol_sm_free(netdev->sm);
|
||||
netdev->sm = NULL;
|
||||
}
|
||||
|
||||
if (over_air)
|
||||
netdev->ap = ft_over_air_sm_new(netdev->handshake,
|
||||
netdev_ft_tx_authenticate,
|
||||
netdev_ft_tx_associate, netdev);
|
||||
else
|
||||
netdev->ap = ft_over_ds_sm_new(netdev->handshake,
|
||||
netdev_ft_over_ds_tx_authenticate,
|
||||
netdev_ft_tx_associate, netdev);
|
||||
|
||||
if (!auth_proto_start(netdev->ap))
|
||||
goto restore_snonce;
|
||||
|
||||
return 0;
|
||||
|
||||
restore_snonce:
|
||||
@ -2864,6 +2935,19 @@ restore_snonce:
|
||||
return err;
|
||||
}
|
||||
|
||||
int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
|
||||
netdev_connect_cb_t cb)
|
||||
{
|
||||
return fast_transition(netdev, target_bss, true, cb);
|
||||
}
|
||||
|
||||
int netdev_fast_transition_over_ds(struct netdev *netdev,
|
||||
struct scan_bss *target_bss,
|
||||
netdev_connect_cb_t cb)
|
||||
{
|
||||
return fast_transition(netdev, target_bss, false, cb);
|
||||
}
|
||||
|
||||
static void netdev_preauth_cb(const uint8_t *pmk, void *user_data)
|
||||
{
|
||||
struct netdev_preauth_state *preauth = user_data;
|
||||
@ -4309,6 +4393,7 @@ struct netdev *netdev_create_from_genl(struct l_genl_msg *msg)
|
||||
const uint8_t action_neighbor_report_prefix[2] = { 0x05, 0x05 };
|
||||
const uint8_t action_sa_query_resp_prefix[2] = { 0x08, 0x01 };
|
||||
const uint8_t action_sa_query_req_prefix[2] = { 0x08, 0x00 };
|
||||
const uint8_t action_ft_response_prefix[] = { 0x06, 0x02 };
|
||||
struct l_io *pae_io = NULL;
|
||||
const struct l_settings *settings = iwd_get_config();
|
||||
bool pae_over_nl80211;
|
||||
@ -4459,6 +4544,10 @@ struct netdev *netdev_create_from_genl(struct l_genl_msg *msg)
|
||||
sizeof(action_sa_query_req_prefix),
|
||||
netdev_sa_query_req_frame_event, NULL);
|
||||
|
||||
netdev_frame_watch_add(netdev, 0x00d0, action_ft_response_prefix,
|
||||
sizeof(action_ft_response_prefix),
|
||||
netdev_ft_response_frame_event, NULL);
|
||||
|
||||
/* Set RSSI threshold for CQM notifications */
|
||||
if (netdev->type == NL80211_IFTYPE_STATION)
|
||||
netdev_cqm_rssi_update(netdev);
|
||||
|
@ -161,6 +161,9 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss,
|
||||
netdev_connect_cb_t cb, void *user_data);
|
||||
int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
|
||||
netdev_connect_cb_t cb);
|
||||
int netdev_fast_transition_over_ds(struct netdev *netdev,
|
||||
struct scan_bss *target_bss,
|
||||
netdev_connect_cb_t cb);
|
||||
int netdev_preauthenticate(struct netdev *netdev, struct scan_bss *target_bss,
|
||||
netdev_preauthenticate_cb_t cb,
|
||||
void *user_data);
|
||||
|
@ -1231,11 +1231,21 @@ static void station_transition_start(struct station *station,
|
||||
return;
|
||||
}
|
||||
|
||||
/* FT-over-DS can be better suited for these situations */
|
||||
if ((hs->mde[4] & 1) && (station->ap_directed_roaming ||
|
||||
station->signal_low)) {
|
||||
if (netdev_fast_transition_over_ds(station->netdev, bss,
|
||||
station_fast_transition_cb) < 0) {
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user