* [PATCH v2 2/6] ft: separate over-air from over-ds initializers
2021-04-29 19:40 [PATCH v2 1/6] ft: create class for FT-over-DS targets James Prestwood
@ 2021-04-29 19:40 ` James Prestwood
2021-04-29 19:41 ` [PATCH v2 3/6] netdev: separate over-air and over-ds netdev APIs James Prestwood
` (4 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: James Prestwood @ 2021-04-29 19:40 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 1880 bytes --]
---
src/ft.c | 27 +++++++++++++++------------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/src/ft.c b/src/ft.c
index 82c790de..ea6eba69 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -923,10 +923,9 @@ static bool ft_start(struct auth_proto *ap)
return true;
}
-static 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,
- bool over_air,
void *user_data)
{
struct ft_sm *ft = l_new(struct ft_sm, 1);
@@ -936,7 +935,7 @@ static struct auth_proto *ft_sm_new(struct handshake_state *hs,
ft->hs = hs;
ft->user_data = user_data;
- ft->ap.rx_authenticate = (over_air) ? ft_rx_authenticate : ft_rx_action;
+ ft->ap.rx_authenticate = ft_rx_authenticate;
ft->ap.rx_associate = ft_rx_associate;
ft->ap.start = ft_start;
ft->ap.free = ft_sm_free;
@@ -944,18 +943,22 @@ static struct auth_proto *ft_sm_new(struct handshake_state *hs,
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);
+ struct ft_sm *ft = l_new(struct ft_sm, 1);
+
+ ft->tx_assoc = tx_assoc;
+ ft->tx_auth = tx_auth;
+ ft->hs = hs;
+ ft->user_data = user_data;
+
+ ft->ap.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;
}
--
2.26.2
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 3/6] netdev: separate over-air and over-ds netdev APIs
2021-04-29 19:40 [PATCH v2 1/6] ft: create class for FT-over-DS targets James Prestwood
2021-04-29 19:40 ` [PATCH v2 2/6] ft: separate over-air from over-ds initializers James Prestwood
@ 2021-04-29 19:41 ` James Prestwood
2021-04-29 19:41 ` [PATCH v2 4/6] ft: netdev: refactor FT-over-DS into two stages James Prestwood
` (3 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: James Prestwood @ 2021-04-29 19:41 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 3867 bytes --]
---
src/netdev.c | 68 ++++++++++++++++++++++++++++++----------------------
1 file changed, 39 insertions(+), 29 deletions(-)
diff --git a/src/netdev.c b/src/netdev.c
index 25891092..232d098d 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -3658,10 +3658,25 @@ static void prepare_ft(struct netdev *netdev, struct scan_bss *target_bss)
{
struct netdev_handshake_state *nhs;
+ /*
+ * We reuse the handshake_state object and reset what's needed.
+ * Could also create a new object and copy most of the state but
+ * we would end up doing more work.
+ */
+ memcpy(netdev->prev_snonce, netdev->handshake->snonce, 32);
+
memcpy(netdev->prev_bssid, netdev->handshake->aa, 6);
netdev->prev_frequency = netdev->frequency;
netdev->frequency = target_bss->frequency;
+ handshake_state_set_authenticator_address(netdev->handshake,
+ target_bss->addr);
+
+ if (target_bss->rsne)
+ handshake_state_set_authenticator_ie(netdev->handshake,
+ target_bss->rsne);
+ memcpy(netdev->handshake->mde + 2, target_bss->mde, 3);
+
netdev->associated = false;
netdev->operational = false;
netdev->in_ft = true;
@@ -3809,8 +3824,7 @@ static const struct wiphy_radio_work_item_ops ft_work_ops = {
.do_work = netdev_ft_work_ready,
};
-static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
- bool over_air,
+int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
netdev_connect_cb_t cb)
{
if (!netdev->operational)
@@ -3823,32 +3837,13 @@ static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
prepare_ft(netdev, target_bss);
- /*
- * We reuse the handshake_state object and reset what's needed.
- * Could also create a new object and copy most of the state but
- * we would end up doing more work.
- */
- memcpy(netdev->prev_snonce, netdev->handshake->snonce, 32);
handshake_state_new_snonce(netdev->handshake);
- handshake_state_set_authenticator_address(netdev->handshake,
- target_bss->addr);
-
- if (target_bss->rsne)
- handshake_state_set_authenticator_ie(netdev->handshake,
- target_bss->rsne);
- memcpy(netdev->handshake->mde + 2, target_bss->mde, 3);
-
netdev->connect_cb = cb;
- if (over_air)
- netdev->ap = ft_over_air_sm_new(netdev->handshake,
+ 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);
wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
&ft_work_ops);
@@ -3856,17 +3851,32 @@ static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
return 0;
}
-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);
+ if (!netdev->operational)
+ return -ENOTCONN;
+
+ if (!netdev->handshake->mde || !target_bss->mde_present ||
+ l_get_le16(netdev->handshake->mde + 2) !=
+ l_get_le16(target_bss->mde))
+ return -EINVAL;
+
+ prepare_ft(netdev, target_bss);
+
+ handshake_state_new_snonce(netdev->handshake);
+
+ netdev->connect_cb = cb;
+
+ netdev->ap = ft_over_ds_sm_new(netdev->handshake,
+ netdev_ft_over_ds_tx_authenticate,
+ netdev_ft_tx_associate, netdev);
+
+ wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
+ &ft_work_ops);
+
+ return 0;
}
static void netdev_preauth_cb(const uint8_t *pmk, void *user_data)
--
2.26.2
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 4/6] ft: netdev: refactor FT-over-DS into two stages
2021-04-29 19:40 [PATCH v2 1/6] ft: create class for FT-over-DS targets James Prestwood
2021-04-29 19:40 ` [PATCH v2 2/6] ft: separate over-air from over-ds initializers James Prestwood
2021-04-29 19:41 ` [PATCH v2 3/6] netdev: separate over-air and over-ds netdev APIs James Prestwood
@ 2021-04-29 19:41 ` James Prestwood
2021-04-29 20:33 ` Denis Kenzior
2021-04-29 19:41 ` [PATCH v2 5/6] station: separate FT-over-DS stages James Prestwood
` (2 subsequent siblings)
5 siblings, 1 reply; 8+ messages in thread
From: James Prestwood @ 2021-04-29 19:41 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 13792 bytes --]
FT-over-DS followed the same pattern as FT-over-Air which worked,
but really limited how the protocol could be used. FT-over-DS is
unique in that we can authenticate to many APs by sending out
FT action frames and parsing the results. Once parsed IWD can
immediately Reassociate, or do so at a later time.
To take advantage of this IWD need to separate FT-over-DS into
two stages: action frame and reassociation.
The initial action frame stage is started by netdev. The target
BSS is sent an FT action frame and a new cache entry is created
in ft.c. Once the response is received the entry is updated
with all the needed data to Reassociate. To limit the record
keeping on netdev each FT-over-DS entry holds a userdata pointer
so netdev doesn't need to maintain its own list of data for
callbacks.
Once the action response is parsed netdev will call back signalling
the action frame sequence was completed (either successfully or not).
At this point the 'normal' FT procedure can start using the
FT-over-DS auth-proto.
---
src/ft.c | 74 ++-----------------
src/ft.h | 1 -
src/netdev.c | 203 ++++++++++++++++++++++++++++++++++++++++-----------
src/netdev.h | 8 ++
4 files changed, 176 insertions(+), 110 deletions(-)
diff --git a/src/ft.c b/src/ft.c
index ea6eba69..56858ee1 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -156,40 +156,6 @@ 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)
@@ -618,34 +584,6 @@ bool ft_over_ds_prepare_handshake(struct ft_ds_info *info,
return true;
}
-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;
- int ret;
-
- 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;
-
- ret = ft_process_ies(ft->hs, ies, ies_len);
- if (ret < 0)
- goto auth_error;
-
- return ft_tx_reassociate(ft);
-
-auth_error:
- return (int)status_code;
-}
-
void ft_ds_info_free(struct ft_ds_info *info)
{
__typeof__(info->free) destroy = info->free;
@@ -833,6 +771,13 @@ static void ft_sm_free(struct auth_proto *ap)
l_free(ft);
}
+static bool ft_over_ds_start(struct auth_proto *ap)
+{
+ struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
+
+ return ft_tx_reassociate(ft) == 0;
+}
+
bool ft_build_authenticate_ies(struct handshake_state *hs,
const uint8_t *new_snonce, uint8_t *buf,
size_t *len)
@@ -944,20 +889,17 @@ struct auth_proto *ft_over_air_sm_new(struct handshake_state *hs,
}
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)
{
struct ft_sm *ft = l_new(struct ft_sm, 1);
ft->tx_assoc = tx_assoc;
- ft->tx_auth = tx_auth;
ft->hs = hs;
ft->user_data = user_data;
- ft->ap.rx_authenticate = ft_rx_action;
ft->ap.rx_associate = ft_rx_associate;
- ft->ap.start = ft_start;
+ ft->ap.start = ft_over_ds_start;
ft->ap.free = ft_sm_free;
return &ft->ap;
diff --git a/src/ft.h b/src/ft.h
index 907c3d5d..7c009f16 100644
--- a/src/ft.h
+++ b/src/ft.h
@@ -57,7 +57,6 @@ struct auth_proto *ft_over_air_sm_new(struct handshake_state *hs,
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);
diff --git a/src/netdev.c b/src/netdev.c
index 232d098d..69fb7174 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -92,6 +92,14 @@ struct netdev_handshake_state {
enum connection_type type;
};
+struct netdev_ft_over_ds_info {
+ struct ft_ds_info super;
+ struct netdev *netdev;
+ struct l_timeout *timeout;
+ netdev_ft_over_ds_cb_t cb;
+ void *user_data;
+};
+
struct netdev {
uint32_t index;
uint64_t wdev_id;
@@ -159,6 +167,8 @@ struct netdev {
struct l_genl_msg *auth_cmd;
struct wiphy_radio_work_item work;
+ struct ft_ds_info *ft_ds_info;
+
bool connected : 1;
bool associated : 1;
bool operational : 1;
@@ -741,6 +751,9 @@ static void netdev_connect_free(struct netdev *netdev)
l_genl_family_cancel(nl80211, netdev->disconnect_cmd_id);
netdev->disconnect_cmd_id = 0;
}
+
+ if (netdev->ft_ds_info)
+ ft_ds_info_free(netdev->ft_ds_info);
}
static void netdev_connect_failed(struct netdev *netdev,
@@ -3719,44 +3732,58 @@ static void prepare_ft(struct netdev *netdev, struct scan_bss *target_bss)
}
}
-static void netdev_ft_request_cb(struct l_genl_msg *msg, void *user_data)
+static void netdev_ft_over_ds_auth_failed(struct ft_ds_info *ft,
+ uint16_t status)
{
- struct netdev *netdev = user_data;
- int err = l_genl_msg_get_error(msg);
+ struct netdev_ft_over_ds_info *info = l_container_of(ft,
+ struct netdev_ft_over_ds_info, super);
+ if (info->cb)
+ info->cb(info->netdev, status, info->super.aa, info->user_data);
- if (err < 0) {
- l_error("Could not send CMD_FRAME (%d)", err);
- netdev_connect_failed(netdev,
- NETDEV_RESULT_AUTHENTICATION_FAILED,
- MMPDU_STATUS_CODE_UNSPECIFIED);
- }
+ ft_ds_info_free(ft);
+
+ info->netdev->ft_ds_info = NULL;
}
static void netdev_ft_response_frame_event(const struct mmpdu_header *hdr,
const void *body, size_t body_len,
int rssi, void *user_data)
{
+ struct netdev *netdev = user_data;
+ struct ft_ds_info *ft;
+ struct netdev_ft_over_ds_info *info;
int ret;
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
- struct netdev *netdev = user_data;
- if (!netdev->ap || !netdev->in_ft)
+ if (!netdev->ft_ds_info)
return;
- ret = auth_proto_rx_authenticate(netdev->ap, body, body_len);
+ ft = netdev->ft_ds_info;
+ info = l_container_of(ft, struct netdev_ft_over_ds_info, super);
+
+ l_timeout_remove(info->timeout);
+ info->timeout = NULL;
+
+ ret = ft_over_ds_parse_action_response(ft, netdev->handshake,
+ body, body_len);
if (ret < 0)
- goto ft_error;
- else if (ret > 0) {
+ return;
+
+ /* Now make sure the packet contained a success status code */
+ if (ret > 0) {
status_code = (uint16_t)ret;
goto ft_error;
}
+ if (info->cb)
+ info->cb(netdev, 0, info->super.aa, info->user_data);
+
return;
ft_error:
- netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
- status_code);
- return;
+ l_debug("FT-over-DS to "MAC" failed", MAC_STR(info->super.aa));
+
+ netdev_ft_over_ds_auth_failed(ft, status_code);
}
static void netdev_qos_map_frame_event(const struct mmpdu_header *hdr,
@@ -3781,29 +3808,6 @@ static void netdev_qos_map_frame_event(const struct mmpdu_header *hdr,
netdev_send_qos_map_set(netdev, body + 4, body_len - 4);
}
-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,
- netdev);
-}
-
static bool netdev_ft_work_ready(struct wiphy_radio_work_item *item)
{
struct netdev *netdev = l_container_of(item, struct netdev, work);
@@ -3855,6 +3859,11 @@ int netdev_fast_transition_over_ds(struct netdev *netdev,
struct scan_bss *target_bss,
netdev_connect_cb_t cb)
{
+ struct ft_ds_info *ft = netdev->ft_ds_info;
+
+ if (!ft)
+ return -ENOENT;
+
if (!netdev->operational)
return -ENOTCONN;
@@ -3865,13 +3874,14 @@ int netdev_fast_transition_over_ds(struct netdev *netdev,
prepare_ft(netdev, target_bss);
- handshake_state_new_snonce(netdev->handshake);
+ if (!ft_over_ds_prepare_handshake(ft, netdev->handshake))
+ return -ENOENT;
netdev->connect_cb = cb;
netdev->ap = ft_over_ds_sm_new(netdev->handshake,
- netdev_ft_over_ds_tx_authenticate,
- netdev_ft_tx_associate, netdev);
+ netdev_ft_tx_associate,
+ netdev);
wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
&ft_work_ops);
@@ -3879,6 +3889,113 @@ int netdev_fast_transition_over_ds(struct netdev *netdev,
return 0;
}
+static void netdev_ft_request_cb(struct l_genl_msg *msg, void *user_data)
+{
+ struct netdev_ft_over_ds_info *info = user_data;
+
+ if (l_genl_msg_get_error(msg) < 0) {
+ l_error("Could not send CMD_FRAME for FT-over-DS");
+ netdev_ft_over_ds_auth_failed(&info->super,
+ MMPDU_STATUS_CODE_UNSPECIFIED);
+ }
+}
+
+static void netdev_ft_over_ds_timeout(struct l_timeout *timeout,
+ void *user_data)
+{
+ struct netdev_ft_over_ds_info *info = user_data;
+
+ l_timeout_remove(info->timeout);
+ info->timeout = NULL;
+
+ l_debug("");
+
+ netdev_ft_over_ds_auth_failed(&info->super,
+ MMPDU_STATUS_CODE_UNSPECIFIED);
+}
+
+static void netdev_ft_ds_info_free(struct ft_ds_info *ft)
+{
+ struct netdev_ft_over_ds_info *info = l_container_of(ft,
+ struct netdev_ft_over_ds_info, super);
+
+ if (info->timeout)
+ l_timeout_remove(info->timeout);
+
+ l_free(info);
+}
+
+int netdev_fast_transition_over_ds_action(struct netdev *netdev,
+ struct scan_bss *target_bss,
+ netdev_ft_over_ds_cb_t cb,
+ void *user_data)
+{
+ struct netdev_ft_over_ds_info *info;
+ uint8_t ft_req[14];
+ struct handshake_state *hs = netdev->handshake;
+ struct iovec iovs[5];
+ uint8_t buf[512];
+ size_t len;
+
+ /* TODO: Just allow single outstanding action frame for now */
+ if (netdev->ft_ds_info)
+ return -EALREADY;
+
+ if (!netdev->operational)
+ return -ENOTCONN;
+
+ if (!netdev->handshake->mde || !target_bss->mde_present ||
+ l_get_le16(netdev->handshake->mde + 2) !=
+ l_get_le16(target_bss->mde))
+ return -EINVAL;
+
+ l_debug("");
+
+ info = l_new(struct netdev_ft_over_ds_info, 1);
+ info->netdev = netdev;
+
+ memcpy(info->super.spa, hs->spa, ETH_ALEN);
+ memcpy(info->super.aa, target_bss->addr, ETH_ALEN);
+ memcpy(info->super.mde, target_bss->mde, sizeof(info->super.mde));
+ l_getrandom(info->super.snonce, 32);
+ info->super.free = netdev_ft_ds_info_free;
+
+ info->cb = cb;
+ info->user_data = user_data;
+
+ ft_req[0] = 6; /* FT category */
+ ft_req[1] = 1; /* FT Request action */
+ memcpy(ft_req + 2, netdev->addr, 6);
+ memcpy(ft_req + 8, info->super.aa, 6);
+
+ iovs[0].iov_base = ft_req;
+ iovs[0].iov_len = sizeof(ft_req);
+
+ if (!ft_build_authenticate_ies(hs, info->super.snonce, buf, &len))
+ goto failed;
+
+ iovs[1].iov_base = buf;
+ iovs[1].iov_len = len;
+
+ iovs[2].iov_base = NULL;
+
+ netdev->ft_ds_info = &info->super;
+
+ info->timeout = l_timeout_create_ms(300, netdev_ft_over_ds_timeout,
+ info, NULL);
+
+ netdev_send_action_framev(netdev, netdev->handshake->aa, iovs, 2,
+ netdev->frequency,
+ netdev_ft_request_cb,
+ info);
+
+ return 0;
+
+failed:
+ l_free(info);
+ return -EIO;
+}
+
static void netdev_preauth_cb(const uint8_t *pmk, void *user_data)
{
struct netdev_preauth_state *preauth = user_data;
diff --git a/src/netdev.h b/src/netdev.h
index 43ea3893..2f7f3647 100644
--- a/src/netdev.h
+++ b/src/netdev.h
@@ -125,6 +125,10 @@ typedef void (*netdev_get_station_cb_t)(
const char *netdev_iftype_to_string(uint32_t iftype);
+typedef void (*netdev_ft_over_ds_cb_t)(struct netdev *netdev,
+ uint16_t status, const uint8_t *bssid,
+ void *user_data);
+
struct wiphy *netdev_get_wiphy(struct netdev *netdev);
const uint8_t *netdev_get_address(struct netdev *netdev);
uint32_t netdev_get_ifindex(struct netdev *netdev);
@@ -158,6 +162,10 @@ 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_action(struct netdev *netdev,
+ struct scan_bss *target_bss,
+ netdev_ft_over_ds_cb_t cb,
+ void *user_data);
int netdev_fast_transition_over_ds(struct netdev *netdev,
struct scan_bss *target_bss,
netdev_connect_cb_t cb);
--
2.26.2
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH v2 4/6] ft: netdev: refactor FT-over-DS into two stages
2021-04-29 19:41 ` [PATCH v2 4/6] ft: netdev: refactor FT-over-DS into two stages James Prestwood
@ 2021-04-29 20:33 ` Denis Kenzior
0 siblings, 0 replies; 8+ messages in thread
From: Denis Kenzior @ 2021-04-29 20:33 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 4252 bytes --]
Hi James,
On 4/29/21 2:41 PM, James Prestwood wrote:
> FT-over-DS followed the same pattern as FT-over-Air which worked,
> but really limited how the protocol could be used. FT-over-DS is
> unique in that we can authenticate to many APs by sending out
> FT action frames and parsing the results. Once parsed IWD can
> immediately Reassociate, or do so at a later time.
>
> To take advantage of this IWD need to separate FT-over-DS into
> two stages: action frame and reassociation.
>
> The initial action frame stage is started by netdev. The target
> BSS is sent an FT action frame and a new cache entry is created
> in ft.c. Once the response is received the entry is updated
> with all the needed data to Reassociate. To limit the record
> keeping on netdev each FT-over-DS entry holds a userdata pointer
> so netdev doesn't need to maintain its own list of data for
> callbacks.
>
> Once the action response is parsed netdev will call back signalling
> the action frame sequence was completed (either successfully or not).
> At this point the 'normal' FT procedure can start using the
> FT-over-DS auth-proto.
> ---
> src/ft.c | 74 ++-----------------
> src/ft.h | 1 -
> src/netdev.c | 203 ++++++++++++++++++++++++++++++++++++++++-----------
> src/netdev.h | 8 ++
> 4 files changed, 176 insertions(+), 110 deletions(-)
>
<snip>
> @@ -3719,44 +3732,58 @@ static void prepare_ft(struct netdev *netdev, struct scan_bss *target_bss)
> }
> }
>
> -static void netdev_ft_request_cb(struct l_genl_msg *msg, void *user_data)
> +static void netdev_ft_over_ds_auth_failed(struct ft_ds_info *ft,
So really you might as well just pass netdev_ft... objects around..
> + uint16_t status)
> {
> - struct netdev *netdev = user_data;
> - int err = l_genl_msg_get_error(msg);
> + struct netdev_ft_over_ds_info *info = l_container_of(ft,
> + struct netdev_ft_over_ds_info, super);
to maybe avoid the need for the container_of. Using the superclass pattern here
is fine, but it is actually overkill since we will not have any other types. So
even just ft_ds_info_init/ft_ds_info_deinit would be fine, but what you have now
fine as well.
> + if (info->cb)
> + info->cb(info->netdev, status, info->super.aa, info->user_data);
>
> - if (err < 0) {
> - l_error("Could not send CMD_FRAME (%d)", err);
> - netdev_connect_failed(netdev,
> - NETDEV_RESULT_AUTHENTICATION_FAILED,
> - MMPDU_STATUS_CODE_UNSPECIFIED);
> - }
> + ft_ds_info_free(ft);
> +
> + info->netdev->ft_ds_info = NULL;
> }
>
> static void netdev_ft_response_frame_event(const struct mmpdu_header *hdr,
> const void *body, size_t body_len,
> int rssi, void *user_data)
> {
> + struct netdev *netdev = user_data;
> + struct ft_ds_info *ft;
> + struct netdev_ft_over_ds_info *info;
> int ret;
> uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
> - struct netdev *netdev = user_data;
>
> - if (!netdev->ap || !netdev->in_ft)
> + if (!netdev->ft_ds_info)
> return;
>
> - ret = auth_proto_rx_authenticate(netdev->ap, body, body_len);
> + ft = netdev->ft_ds_info;
> + info = l_container_of(ft, struct netdev_ft_over_ds_info, super);
isn't just &ft_ds_info->super simpler?
> +
> + l_timeout_remove(info->timeout);
> + info->timeout = NULL;
> +
> + ret = ft_over_ds_parse_action_response(ft, netdev->handshake,
> + body, body_len);
So how do you know it isn't just a rogue frame?
> if (ret < 0)
> - goto ft_error;
> - else if (ret > 0) {
> + return;
> +
> + /* Now make sure the packet contained a success status code */
> + if (ret > 0) {
> status_code = (uint16_t)ret;
> goto ft_error;
> }
>
> + if (info->cb)
> + info->cb(netdev, 0, info->super.aa, info->user_data);
> +
> return;
>
> ft_error:
> - netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
> - status_code);
> - return;
> + l_debug("FT-over-DS to "MAC" failed", MAC_STR(info->super.aa));
> +
> + netdev_ft_over_ds_auth_failed(ft, status_code);
> }
>
> static void netdev_qos_map_frame_event(const struct mmpdu_header *hdr,
<snip>
Regards,
-Denis
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 5/6] station: separate FT-over-DS stages
2021-04-29 19:40 [PATCH v2 1/6] ft: create class for FT-over-DS targets James Prestwood
` (2 preceding siblings ...)
2021-04-29 19:41 ` [PATCH v2 4/6] ft: netdev: refactor FT-over-DS into two stages James Prestwood
@ 2021-04-29 19:41 ` James Prestwood
2021-04-29 19:41 ` [PATCH v2 6/6] ft: netdev: add return value to tx_associate James Prestwood
2021-04-29 20:23 ` [PATCH v2 1/6] ft: create class for FT-over-DS targets Denis Kenzior
5 siblings, 0 replies; 8+ messages in thread
From: James Prestwood @ 2021-04-29 19:41 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 3578 bytes --]
FT-over-DS was refactored to separate the FT action frame and
reassociation. From stations standpoint IWD needs to call
netdev_fast_transition_over_ds_action prior to actually roaming.
For now these two stages are being combined and the action
roam happens immediately after the action response callback.
---
src/station.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 52 insertions(+), 4 deletions(-)
diff --git a/src/station.c b/src/station.c
index c89b1901..6a308cfd 100644
--- a/src/station.c
+++ b/src/station.c
@@ -113,6 +113,7 @@ struct station {
bool ap_directed_roaming : 1;
bool scanning : 1;
bool autoconnect : 1;
+ bool ft_over_ds : 1;
};
struct anqp_entry {
@@ -1587,10 +1588,17 @@ static void station_roam_failed(struct station *station)
l_debug("%u", netdev_get_ifindex(station->netdev));
/*
- * If we attempted a reassociation or a fast transition, and ended up
- * here then we are now disconnected.
+ * If we attempted a reassociation or a fast transition (except DS),
+ * and ended up here then we are now disconnected. In the case of
+ * FT over DS we can remain connected to the AP even if the transition
+ * fails.
*/
if (station->state == STATION_STATE_ROAMING) {
+ if (station->ft_over_ds) {
+ station_enter_state(station, STATION_STATE_CONNECTED);
+ goto delayed_retry;
+ }
+
station_disassociated(station);
return;
}
@@ -1628,6 +1636,7 @@ delayed_retry:
station->preparing_roam = false;
station->roam_scan_full = false;
station->ap_directed_roaming = false;
+ station->ft_over_ds = false;
if (station->signal_low)
station_roam_timeout_rearm(station, roam_retry_interval);
@@ -1713,6 +1722,41 @@ static bool bss_match_bssid(const void *a, const void *b)
return !memcmp(bss->addr, bssid, sizeof(bss->addr));
}
+static void station_fast_transition_ds_cb(struct netdev *netdev,
+ uint16_t status, const uint8_t *bssid,
+ void *user_data)
+{
+ struct station *station = user_data;
+ struct scan_bss *bss;
+
+ if (status != 0)
+ goto failed;
+
+ /*
+ * TODO: In the future it may be desired to start sending out these
+ * FT-over-DS action frames at the time of connecting then be able to
+ * roam immediately when required. If this is being done we can simply
+ * bail out now as ft already caches the entires. But since this was
+ * initiated due to a need to roam, do so now.
+ */
+
+ /* Make sure we still have our BSS */
+ bss = l_queue_find(station->bss_list, bss_match_bssid, bssid);
+ if (!bss)
+ goto failed;
+
+ l_debug("Starting FT-over-DS roam");
+
+ if (netdev_fast_transition_over_ds(station->netdev, bss,
+ station_fast_transition_cb) < 0)
+ goto failed;
+
+ return;
+
+failed:
+ station_roam_failed(station);
+}
+
static void station_preauthenticate_cb(struct netdev *netdev,
enum netdev_result result,
const uint8_t *pmk, void *user_data)
@@ -1810,8 +1854,12 @@ static void station_transition_start(struct station *station,
/* 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->ft_over_ds = true;
+
+ if (netdev_fast_transition_over_ds_action(
+ station->netdev, bss,
+ station_fast_transition_ds_cb,
+ station) < 0) {
station_roam_failed(station);
return;
}
--
2.26.2
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 6/6] ft: netdev: add return value to tx_associate
2021-04-29 19:40 [PATCH v2 1/6] ft: create class for FT-over-DS targets James Prestwood
` (3 preceding siblings ...)
2021-04-29 19:41 ` [PATCH v2 5/6] station: separate FT-over-DS stages James Prestwood
@ 2021-04-29 19:41 ` James Prestwood
2021-04-29 20:23 ` [PATCH v2 1/6] ft: create class for FT-over-DS targets Denis Kenzior
5 siblings, 0 replies; 8+ messages in thread
From: James Prestwood @ 2021-04-29 19:41 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 2443 bytes --]
Prior to this, an error sending the FT Reassociation was treated
as fatal, which is correct for FT-over-Air but not for FT-over-DS.
If the actual l_genl_family_send call fails for FT-over-DS the
existing connection can be maintained and there is no need to
call netdev_connect_failed.
Adding a return to the tx_associate function works for both FT
types. In the FT-over-Air case this return will ultimately get
sent back up to auth_proto_rx_authenticate in which case will
call netdev_connect_failed. For FT-over-DS tx_associate is
actually called from the 'start' operation which can fail and
still maintain the existing connection.
---
src/ft.c | 4 +---
src/ft.h | 2 +-
src/netdev.c | 8 ++++----
3 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/src/ft.c b/src/ft.c
index 56858ee1..f6ea1425 100644
--- a/src/ft.c
+++ b/src/ft.c
@@ -287,9 +287,7 @@ static int ft_tx_reassociate(struct ft_sm *ft)
iov_elems += 1;
}
- ft->tx_assoc(iov, iov_elems, ft->user_data);
-
- return 0;
+ return ft->tx_assoc(iov, iov_elems, ft->user_data);
error:
return -EINVAL;
diff --git a/src/ft.h b/src/ft.h
index 7c009f16..3d1cfe30 100644
--- a/src/ft.h
+++ b/src/ft.h
@@ -24,7 +24,7 @@ struct handshake_state;
typedef void (*ft_tx_authenticate_func_t)(struct iovec *iov, size_t iov_len,
void *user_data);
-typedef void (*ft_tx_associate_func_t)(struct iovec *ie_iov, size_t iov_len,
+typedef int (*ft_tx_associate_func_t)(struct iovec *ie_iov, size_t iov_len,
void *user_data);
typedef void (*ft_ds_free_func_t)(void *user_data);
diff --git a/src/netdev.c b/src/netdev.c
index 69fb7174..015a5237 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -3643,7 +3643,7 @@ restore_snonce:
MMPDU_STATUS_CODE_UNSPECIFIED);
}
-static void netdev_ft_tx_associate(struct iovec *ie_iov, size_t iov_len,
+static int netdev_ft_tx_associate(struct iovec *ie_iov, size_t iov_len,
void *user_data)
{
struct netdev *netdev = user_data;
@@ -3661,10 +3661,10 @@ static void netdev_ft_tx_associate(struct iovec *ie_iov, size_t iov_len,
if (!netdev->connect_cmd_id) {
l_genl_msg_unref(msg);
- netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
- MMPDU_STATUS_CODE_UNSPECIFIED);
- return;
+ return -EIO;
}
+
+ return 0;
}
static void prepare_ft(struct netdev *netdev, struct scan_bss *target_bss)
--
2.26.2
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH v2 1/6] ft: create class for FT-over-DS targets
2021-04-29 19:40 [PATCH v2 1/6] ft: create class for FT-over-DS targets James Prestwood
` (4 preceding siblings ...)
2021-04-29 19:41 ` [PATCH v2 6/6] ft: netdev: add return value to tx_associate James Prestwood
@ 2021-04-29 20:23 ` Denis Kenzior
5 siblings, 0 replies; 8+ messages in thread
From: Denis Kenzior @ 2021-04-29 20:23 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 6313 bytes --]
Hi James,
On 4/29/21 2:40 PM, James Prestwood wrote:
> FT-over-DS is being separated into two independent stages. The
> first of which is the processing of the action frame response.
> This new class will hold all the parsed information from the action
> frame and allowing it to be retrieved at a later time when IWD
> needs to roam.
>
> Initial info class should be created when the action frame is
> being sent out. Once a response is received it can be parsed
> with ft_over_ds_parse_action_response. This verifies the frame
> and updates the ft_ds_info class with the parsed data.
>
> ft_over_ds_prepare_handshake is the final step prior to
> Reassociation. This sets all the stored IEs, anonce, and KH IDs
> into the handshake and derives the new PTK.
> ---
> src/ft.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> src/ft.h | 25 +++++++++++++
> 2 files changed, 131 insertions(+)
>
> diff --git a/src/ft.c b/src/ft.c
> index 85d2d369..82c790de 100644
> --- a/src/ft.c
> +++ b/src/ft.c
> @@ -465,6 +465,52 @@ static bool ft_parse_fte(struct handshake_state *hs,
> return true;
> }
>
> +static bool ft_over_ds_process_ies(struct ft_ds_info *info,
> + struct handshake_state *hs,
> + const uint8_t *ies,
> + size_t ies_len)
> +{
> + const uint8_t *rsne = NULL;
> + const uint8_t *mde = NULL;
> + const uint8_t *fte = NULL;
> + bool is_rsn;
> +
> + if (ft_parse_ies(ies, ies_len, &rsne, &mde, &fte) < 0)
> + return false;
> +
> + is_rsn = hs->supplicant_ie != NULL;
> +
> + if (is_rsn) {
> + if (!ft_verify_rsne(rsne, hs->pmk_r0_name,
> + hs->authenticator_ie))
> + goto ft_error;
> + } else if (rsne)
> + goto ft_error;
> +
> + /*
> + * Check for an MD IE identical to the one we sent in message 1
> + *
> + * 12.8.3: "The MDE shall contain the MDID and FT Capability and
> + * Policy fields. This element shall be the same as the MDE
> + * advertised by the target AP in Beacon and Probe Response frames."
> + */
> + if (!mde || memcmp(info->mde, mde + 2, sizeof(info->mde)))
> + goto ft_error;
> +
> + if (is_rsn) {
> + if (!ft_parse_fte(hs, info->snonce, fte, &info->ft_info))
> + goto ft_error;
> +
> + info->fte = l_memdup(fte, fte[1] + 2);
> + } else if (fte)
> + goto ft_error;
So this seems like a copy-paste of ft_process_ies. Maybe they should be unified
somehow.
> +
> + return true;
> +
> +ft_error:
> + return false;
> +}
> +
> static int ft_process_ies(struct handshake_state *hs, const uint8_t *ies,
> size_t ies_len)
> {
> @@ -523,6 +569,55 @@ ft_error:
> return -EBADMSG;
> }
>
> +int ft_over_ds_parse_action_response(struct ft_ds_info *info,
> + struct handshake_state *hs,
> + const uint8_t *frame, size_t frame_len)
> +{
> + uint16_t status = 0;
> +
> + if (frame_len < 16)
> + goto parse_error;
So this results in a 0 being returned? I'm not sure that this is intended?
> +
> + /* Category FT */
> + if (frame[0] != 6)
> + goto parse_error;
> +
This should really return different error types based on what actually happened.
i.e. -EINVAL
> + /* FT Action */
> + if (frame[1] != 2)
> + goto parse_error;
> +
> + if (memcmp(frame + 2, info->spa, 6))
> + goto parse_error;
-ENOENT
> + if (memcmp(frame + 8, info->aa, 6))
> + goto parse_error;
-ENOENT or something
> +
> + if (!ft_over_ds_process_ies(info, hs, frame + 16, frame_len - 16))
> + goto parse_error;
EBADMSG
> +
> + return 0;
> +
> +parse_error:
> + return (int)status;
> +}
> +
> +bool ft_over_ds_prepare_handshake(struct ft_ds_info *info,
> + struct handshake_state *hs)
> +{
> + memcpy(hs->snonce, info->snonce, sizeof(hs->snonce));
> +
> + handshake_state_set_fte(hs, info->fte);
> +
> + handshake_state_set_anonce(hs, info->ft_info.anonce);
> +
> + handshake_state_set_kh_ids(hs, info->ft_info.r0khid,
> + info->ft_info.r0khid_len,
> + info->ft_info.r1khid);
> +
> + handshake_state_derive_ptk(hs);
> +
This also seems to be straight out of ft_process_ies...? Does this need to be
done only if the RSN is present? Have you tested this on Open networks?
> + return true;
> +}
> +
> static int ft_rx_action(struct auth_proto *ap, const uint8_t *frame,
> size_t frame_len)
> {
> @@ -551,6 +646,17 @@ auth_error:
> return (int)status_code;
> }
>
> +void ft_ds_info_free(struct ft_ds_info *info)
> +{
> + __typeof__(info->free) destroy = info->free;
> +
> + if (info->fte)
> + l_free(info->fte);
> +
> + if (destroy)
> + destroy(info);
> +}
> +
> static int ft_rx_authenticate(struct auth_proto *ap, const uint8_t *frame,
> size_t frame_len)
> {
> diff --git a/src/ft.h b/src/ft.h
> index f24b3b5e..907c3d5d 100644
> --- a/src/ft.h
> +++ b/src/ft.h
> @@ -20,15 +20,37 @@
> *
> */
>
> +struct handshake_state;
> +
> typedef void (*ft_tx_authenticate_func_t)(struct iovec *iov, size_t iov_len,
> void *user_data);
> typedef void (*ft_tx_associate_func_t)(struct iovec *ie_iov, size_t iov_len,
> void *user_data);
>
> +typedef void (*ft_ds_free_func_t)(void *user_data);
> +
> +struct ft_ds_info {
> + uint8_t spa[6];
> + uint8_t aa[6];
> + uint8_t snonce[32];
> + uint8_t mde[3];
> + uint8_t *fte;
> +
> + struct ie_ft_info ft_info;
> +
> + void (*free)(struct ft_ds_info *s);
> +};
> +
> +void ft_ds_info_free(struct ft_ds_info *info);
> +
> bool ft_build_authenticate_ies(struct handshake_state *hs,
> const uint8_t *new_snonce, uint8_t *buf,
> size_t *len);
>
> +int ft_over_ds_parse_action_response(struct ft_ds_info *info,
> + struct handshake_state *hs,
> + const uint8_t *frame, size_t frame_len);
> +
> 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,
> @@ -38,3 +60,6 @@ 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);
> +
> +bool ft_over_ds_prepare_handshake(struct ft_ds_info *info,
> + struct handshake_state *hs);
>
Regards,
-Denis
^ permalink raw reply [flat|nested] 8+ messages in thread