diff --git a/hostapd/Android.mk b/hostapd/Android.mk index 31dec1750..f2c498dd3 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -260,6 +260,9 @@ OBJS += src/fst/fst_group.c OBJS += src/fst/fst_iface.c OBJS += src/fst/fst_session.c OBJS += src/fst/fst_ctrl_aux.c +ifdef CONFIG_FST_TEST +L_CFLAGS += -DCONFIG_FST_TEST +endif ifndef CONFIG_NO_CTRL_IFACE OBJS += src/fst/fst_ctrl_iface.c endif diff --git a/hostapd/Makefile b/hostapd/Makefile index c543d6674..086a6412e 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -905,6 +905,9 @@ OBJS += ../src/fst/fst_group.o OBJS += ../src/fst/fst_iface.o OBJS += ../src/fst/fst_session.o OBJS += ../src/fst/fst_ctrl_aux.o +ifdef CONFIG_FST_TEST +CFLAGS += -DCONFIG_FST_TEST +endif ifndef CONFIG_NO_CTRL_IFACE OBJS += ../src/fst/fst_ctrl_iface.o endif diff --git a/hostapd/defconfig b/hostapd/defconfig index f8c6a7dcc..62a0edeea 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -286,6 +286,9 @@ CONFIG_IPV6=y # Enable Fast Session Transfer (FST) #CONFIG_FST=y +# Enable CLI commands for FST testing +#CONFIG_FST_TEST=y + # Testing options # This can be used to enable some testing options (see also the example # configuration file) that are really useful only for testing clients that diff --git a/src/fst/fst_ctrl_defs.h b/src/fst/fst_ctrl_defs.h index 0e883b1c0..67353890f 100644 --- a/src/fst/fst_ctrl_defs.h +++ b/src/fst/fst_ctrl_defs.h @@ -67,6 +67,20 @@ #define FST_CMD_SESSION_TRANSFER "session_transfer" #define FST_CMD_SESSION_TEARDOWN "session_teardown" +#ifdef CONFIG_FST_TEST +#define FST_CTR_PVAL_BAD_NEW_BAND "bad_new_band" + +#define FST_CMD_TEST_REQUEST "test_request" +#define FST_CTR_IS_SUPPORTED "is_supported" +#define FST_CTR_SEND_SETUP_REQUEST "send_setup_request" +#define FST_CTR_SEND_SETUP_RESPONSE "send_setup_response" +#define FST_CTR_SEND_ACK_REQUEST "send_ack_request" +#define FST_CTR_SEND_ACK_RESPONSE "send_ack_response" +#define FST_CTR_SEND_TEAR_DOWN "send_tear_down" +#define FST_CTR_GET_FSTS_ID "get_fsts_id" +#define FST_CTR_GET_LOCAL_MBIES "get_local_mbies" +#endif /* CONFIG_FST_TEST */ + /* Events */ #define FST_CTRL_EVENT_IFACE "FST-EVENT-IFACE" #define FST_CEI_PNAME_IFNAME "ifname" diff --git a/src/fst/fst_ctrl_iface.c b/src/fst/fst_ctrl_iface.c index 6d7887e56..db68bb50c 100644 --- a/src/fst/fst_ctrl_iface.c +++ b/src/fst/fst_ctrl_iface.c @@ -408,6 +408,58 @@ static int session_teardown(const char *session_id, char *buf, size_t buflen) return os_snprintf(buf, buflen, "OK\n"); } + +#ifdef CONFIG_FST_TEST +/* fst test_request */ +static int test_request(const char *request, char *buf, size_t buflen) +{ + const char *p = request; + int ret; + + if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_REQUEST, + os_strlen(FST_CTR_SEND_SETUP_REQUEST))) { + ret = fst_test_req_send_fst_request( + p + os_strlen(FST_CTR_SEND_SETUP_REQUEST)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_RESPONSE, + os_strlen(FST_CTR_SEND_SETUP_RESPONSE))) { + ret = fst_test_req_send_fst_response( + p + os_strlen(FST_CTR_SEND_SETUP_RESPONSE)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_REQUEST, + os_strlen(FST_CTR_SEND_ACK_REQUEST))) { + ret = fst_test_req_send_ack_request( + p + os_strlen(FST_CTR_SEND_ACK_REQUEST)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_RESPONSE, + os_strlen(FST_CTR_SEND_ACK_RESPONSE))) { + ret = fst_test_req_send_ack_response( + p + os_strlen(FST_CTR_SEND_ACK_RESPONSE)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_TEAR_DOWN, + os_strlen(FST_CTR_SEND_TEAR_DOWN))) { + ret = fst_test_req_send_tear_down( + p + os_strlen(FST_CTR_SEND_TEAR_DOWN)); + } else if (!os_strncasecmp(p, FST_CTR_GET_FSTS_ID, + os_strlen(FST_CTR_GET_FSTS_ID))) { + u32 fsts_id = fst_test_req_get_fsts_id( + p + os_strlen(FST_CTR_GET_FSTS_ID)); + if (fsts_id != FST_FSTS_ID_NOT_FOUND) + return os_snprintf(buf, buflen, "%u\n", fsts_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } else if (!os_strncasecmp(p, FST_CTR_GET_LOCAL_MBIES, + os_strlen(FST_CTR_GET_LOCAL_MBIES))) { + return fst_test_req_get_local_mbies( + p + os_strlen(FST_CTR_GET_LOCAL_MBIES), buf, buflen); + } else if (!os_strncasecmp(p, FST_CTR_IS_SUPPORTED, + os_strlen(FST_CTR_IS_SUPPORTED))) { + ret = 0; + } else { + fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK"); +} +#endif /* CONFIG_FST_TEST */ + + /* fst list_sessions */ struct list_sessions_cb_ctx { char *buf; @@ -752,6 +804,9 @@ int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size) { FST_CMD_SESSION_RESPOND, 1, session_respond}, { FST_CMD_SESSION_TRANSFER, 1, session_transfer}, { FST_CMD_SESSION_TEARDOWN, 1, session_teardown}, +#ifdef CONFIG_FST_TEST + { FST_CMD_TEST_REQUEST, 1, test_request }, +#endif /* CONFIG_FST_TEST */ { NULL, 0, NULL } }; const struct fst_command *c; diff --git a/src/fst/fst_session.c b/src/fst/fst_session.c index da994d8ea..0f5d72607 100644 --- a/src/fst/fst_session.c +++ b/src/fst/fst_session.c @@ -14,6 +14,9 @@ #include "fst/fst_internal.h" #include "fst/fst_defs.h" #include "fst/fst_ctrl_iface.h" +#ifdef CONFIG_FST_TEST +#include "fst/fst_ctrl_defs.h" +#endif /* CONFIG_FST_TEST */ #define US_80211_TU 1024 @@ -1272,3 +1275,285 @@ struct fst_session * fst_session_global_get_first_by_group(struct fst_group *g) return NULL; } + +#ifdef CONFIG_FST_TEST + +static int get_group_fill_session(struct fst_group **g, struct fst_session *s) +{ + const u8 *old_addr, *new_addr; + struct fst_get_peer_ctx *ctx; + + os_memset(s, 0, sizeof(*s)); + *g = dl_list_first(&fst_global_groups_list, + struct fst_group, global_groups_lentry); + if (!*g) + return EINVAL; + + s->data.new_iface = dl_list_first(&(*g)->ifaces, struct fst_iface, + group_lentry); + if (!s->data.new_iface) + return EINVAL; + + s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next, + struct fst_iface, group_lentry); + if (!s->data.old_iface) + return EINVAL; + + old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE); + if (!old_addr) + return EINVAL; + + new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE); + if (!new_addr) + return EINVAL; + + os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN); + os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN); + + return 0; +} + + +#define FST_MAX_COMMAND_WORD_NAME_LENGTH 16 + +int fst_test_req_send_fst_request(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_setup_req req; + struct fst_session s; + struct fst_group *g; + enum hostapd_hw_mode hw_mode; + u8 channel; + char additional_param[FST_MAX_COMMAND_WORD_NAME_LENGTH]; + + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return EINVAL; + + if (get_group_fill_session(&g, &s)) + return EINVAL; + + req.action = FST_ACTION_SETUP_REQUEST; + req.dialog_token = g->dialog_token; + req.llt = host_to_le32(FST_LLT_MS_DEFAULT); + /* 8.4.2.147 Session Transition element */ + req.stie.element_id = WLAN_EID_SESSION_TRANSITION; + req.stie.length = sizeof(req.stie); + req.stie.fsts_id = host_to_le32(fsts_id); + req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); + + fst_iface_get_channel_info(s.data.new_iface, &hw_mode, &channel); + req.stie.new_band_id = fst_hw_mode_to_band(hw_mode); + req.stie.new_band_op = 1; + req.stie.new_band_setup = 0; + + fst_iface_get_channel_info(s.data.old_iface, &hw_mode, &channel); + req.stie.old_band_id = fst_hw_mode_to_band(hw_mode); + req.stie.old_band_op = 1; + req.stie.old_band_setup = 0; + + if (!fst_read_next_text_param(endp, additional_param, + sizeof(additional_param), &endp)) { + if (!os_strcasecmp(additional_param, FST_CTR_PVAL_BAD_NEW_BAND)) + req.stie.new_band_id = req.stie.old_band_id; + } + + return fst_session_send_action(&s, TRUE, &req, sizeof(req), + s.data.old_iface->mb_ie); +} + + +int fst_test_req_send_fst_response(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_setup_res res; + struct fst_session s; + struct fst_group *g; + enum hostapd_hw_mode hw_mode; + u8 status_code; + u8 channel; + char response[FST_MAX_COMMAND_WORD_NAME_LENGTH]; + struct fst_session *_s; + + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return EINVAL; + + if (get_group_fill_session(&g, &s)) + return EINVAL; + + status_code = WLAN_STATUS_SUCCESS; + if (!fst_read_next_text_param(endp, response, sizeof(response), + &endp)) { + if (!os_strcasecmp(response, FST_CS_PVAL_RESPONSE_REJECT)) + status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION; + } + + os_memset(&res, 0, sizeof(res)); + + res.action = FST_ACTION_SETUP_RESPONSE; + /* + * If some session has just received an FST Setup Request, then + * use the correct dialog token copied from this request. + */ + _s = fst_find_session_in_progress(fst_session_get_peer_addr(&s, TRUE), + g); + res.dialog_token = (_s && fst_session_is_ready_pending(_s)) ? + _s->data.pending_setup_req_dlgt : g->dialog_token; + res.status_code = status_code; + + if (res.status_code == WLAN_STATUS_SUCCESS) { + res.stie.element_id = WLAN_EID_SESSION_TRANSITION; + res.stie.length = sizeof(res.stie); + res.stie.fsts_id = fsts_id; + res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); + + fst_iface_get_channel_info(s.data.new_iface, &hw_mode, + &channel); + res.stie.new_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.new_band_op = 1; + res.stie.new_band_setup = 0; + + fst_iface_get_channel_info(s.data.old_iface, &hw_mode, + &channel); + res.stie.old_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.old_band_op = 1; + res.stie.old_band_setup = 0; + } + + if (!fst_read_next_text_param(endp, response, sizeof(response), + &endp)) { + if (!os_strcasecmp(response, FST_CTR_PVAL_BAD_NEW_BAND)) + res.stie.new_band_id = res.stie.old_band_id; + } + + return fst_session_send_action(&s, TRUE, &res, sizeof(res), + s.data.old_iface->mb_ie); +} + + +int fst_test_req_send_ack_request(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_ack_req req; + struct fst_session s; + struct fst_group *g; + + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return EINVAL; + + if (get_group_fill_session(&g, &s)) + return EINVAL; + + os_memset(&req, 0, sizeof(req)); + req.action = FST_ACTION_ACK_REQUEST; + req.dialog_token = g->dialog_token; + req.fsts_id = fsts_id; + + return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL); +} + + +int fst_test_req_send_ack_response(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_ack_res res; + struct fst_session s; + struct fst_group *g; + + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return EINVAL; + + if (get_group_fill_session(&g, &s)) + return EINVAL; + + os_memset(&res, 0, sizeof(res)); + res.action = FST_ACTION_ACK_RESPONSE; + res.dialog_token = g->dialog_token; + res.fsts_id = fsts_id; + + return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL); +} + + +int fst_test_req_send_tear_down(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_tear_down td; + struct fst_session s; + struct fst_group *g; + + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return EINVAL; + + if (get_group_fill_session(&g, &s)) + return EINVAL; + + os_memset(&td, 0, sizeof(td)); + td.action = FST_ACTION_TEAR_DOWN; + td.fsts_id = fsts_id; + + return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL); +} + + +u32 fst_test_req_get_fsts_id(const char *params) +{ + int sid; + Boolean is_valid; + char *endp; + struct fst_session *s; + + sid = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return FST_FSTS_ID_NOT_FOUND; + + s = fst_session_get_by_id(sid); + if (!s) + return FST_FSTS_ID_NOT_FOUND; + + return s->data.fsts_id; +} + + +int fst_test_req_get_local_mbies(const char *request, char *buf, size_t buflen) +{ + char *endp; + char ifname[FST_MAX_COMMAND_WORD_NAME_LENGTH]; + struct fst_group *g; + struct fst_iface *iface; + int ret; + + if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) || + !*ifname) + goto problem; + g = dl_list_first(&fst_global_groups_list, struct fst_group, + global_groups_lentry); + if (!g) + goto problem; + iface = fst_group_get_iface_by_name(g, ifname); + if (!iface || !iface->mb_ie) + goto problem; + ret = print_mb_ies(iface->mb_ie, buf, buflen); + if ((size_t) ret != wpabuf_len(iface->mb_ie) * 2) + fst_printf(MSG_WARNING, "MB IEs copied only partially"); + return ret; + +problem: + return os_snprintf(buf, buflen, "FAIL\n"); +} + +#endif /* CONFIG_FST_TEST */ diff --git a/src/fst/fst_session.h b/src/fst/fst_session.h index 4370d2cbf..1162de4b3 100644 --- a/src/fst/fst_session.h +++ b/src/fst/fst_session.h @@ -62,4 +62,19 @@ int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac, Boolean is_old); int fst_session_set_str_llt(struct fst_session *s, const char *llt_str); +#ifdef CONFIG_FST_TEST + +#define FST_FSTS_ID_NOT_FOUND ((u32) -1) + +int fst_test_req_send_fst_request(const char *params); +int fst_test_req_send_fst_response(const char *params); +int fst_test_req_send_ack_request(const char *params); +int fst_test_req_send_ack_response(const char *params); +int fst_test_req_send_tear_down(const char *params); +u32 fst_test_req_get_fsts_id(const char *params); +int fst_test_req_get_local_mbies(const char *request, char *buf, + size_t buflen); + +#endif /* CONFIG_FST_TEST */ + #endif /* FST_SESSION_H */ diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index 65306d23c..378d763df 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -323,6 +323,9 @@ OBJS += src/fst/fst_session.c OBJS += src/fst/fst_iface.c OBJS += src/fst/fst_group.c OBJS += src/fst/fst_ctrl_aux.c +ifdef CONFIG_FST_TEST +L_CFLAGS += -DCONFIG_FST_TEST +endif ifdef CONFIG_CTRL_IFACE OBJS += src/fst/fst_ctrl_iface.c endif diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 43e45d563..a75fc0b4b 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -1593,6 +1593,9 @@ endif ifdef CONFIG_FST CFLAGS += -DCONFIG_FST +ifdef CONFIG_FST_TEST +CFLAGS += -DCONFIG_FST_TEST +endif FST_OBJS += ../src/fst/fst.o FST_OBJS += ../src/fst/fst_session.o FST_OBJS += ../src/fst/fst_iface.o diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index 57672aeeb..d714a691d 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -498,3 +498,6 @@ CONFIG_PEERKEY=y # Enable Fast Session Transfer (FST) #CONFIG_FST=y + +# Enable CLI commands for FST testing +#CONFIG_FST_TEST=y