From: David Spinadel <david.spinadel@xxxxxxxxx> Add an API to hostapd to request LCI from an associated station using radio measurement. Signed-off-by: David Spinadel <david.spinadel@xxxxxxxxx> --- hostapd/ctrl_iface.c | 19 ++++++ hostapd/hostapd_cli.c | 19 ++++++ src/ap/hostapd.c | 3 +- src/ap/hostapd.h | 3 + src/ap/rrm.c | 158 +++++++++++++++++++++++++++++++++++++++++++ src/ap/rrm.h | 3 + src/common/ieee802_11_defs.h | 2 + 7 files changed, 206 insertions(+), 1 deletion(-) diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index bfe3731..9a188b7 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -53,6 +53,7 @@ #include "config_file.h" #include "ctrl_iface.h" #include "ap/neighbor_db.h" +#include "ap/rrm.h" #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256 @@ -2072,6 +2073,21 @@ static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd, #endif /* NEED_AP_MLME */ +static int hostapd_ctrl_iface_req_lci(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_ERROR, + "CTRL: Request LCI: Inavalid MAC address"); + return -1; + } + + return hostapd_send_lci_req(hapd, addr); +} + + static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf) { struct wpa_ssid_value ssid; @@ -2442,6 +2458,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) { if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16)) reply_len = -1; + } else if (os_strncmp(buf, "REQ_LCI ", 8) == 0) { + if (hostapd_ctrl_iface_req_lci(hapd, buf + 8)) + reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 863db42..b2e0de1 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1139,6 +1139,24 @@ static int hostapd_cli_cmd_remove_neighbor(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_req_lci(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid req_lci command - requires destination address\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "REQ_LCI %s", argv[0]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long REQ_LCI command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -1250,6 +1268,7 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { { "pmksa_flush", hostapd_cli_cmd_pmksa_flush }, { "set_neighbor", hostapd_cli_cmd_set_neighbor }, { "remove_neighbor", hostapd_cli_cmd_remove_neighbor }, + { "req_lci", hostapd_cli_req_lci }, { NULL, NULL } }; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 7e30fa9..99ba768 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -44,6 +44,7 @@ #include "dhcp_snoop.h" #include "ndisc_snoop.h" #include "neighbor_db.h" +#include "rrm.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -337,7 +338,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) hapd->mesh_pending_auth = NULL; #endif /* CONFIG_MESH */ - hostpad_free_neighbor_db(hapd); + hostapd_clean_rrm(hapd); } diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index e2c4913..c3d7807 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -298,6 +298,9 @@ struct hostapd_data { #endif /* CONFIG_MBO */ struct dl_list nr_db; + + u8 lci_req_token; + int lci_req_active; }; diff --git a/src/ap/rrm.c b/src/ap/rrm.c index 7df25ae..3aaa2cf 100644 --- a/src/ap/rrm.c +++ b/src/ap/rrm.c @@ -11,6 +11,70 @@ #include "utils/common.h" #include "hostapd.h" #include "ap_drv_ops.h" +#include "sta_info.h" +#include "eloop.h" +#include "neighbor_db.h" + +#define HOSTAPD_RRM_REQUEST_TIMEOUT 5 + + +static void hoastapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_data; + + wpa_printf(MSG_DEBUG, "RRM: LCI request (token %d) timed out", + hapd->lci_req_token); + hapd->lci_req_active = 0; +} + + +static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token, + const u8 *pos, size_t len) +{ + if (!hapd->lci_req_active || hapd->lci_req_token != token) { + wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %d", token); + } else { + hapd->lci_req_active = 0; + eloop_cancel_timeout(hoastapd_lci_rep_timeout_handler, hapd, + NULL); + wpa_printf(MSG_DEBUG, "LCI report token %d len %zu", token, + len); + } +} + + +static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd, + const u8 *buf, size_t len) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + const u8 *pos, *ie; + u8 token; + + token = mgmt->u.action.u.rrm.dialog_token; + pos = mgmt->u.action.u.rrm.variable; + + while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REPORT))) { + if (ie[1] < 5) { + wpa_printf(MSG_DEBUG, "Bad measure report IE"); + break; + } + + wpa_printf(MSG_DEBUG, "Measurement report type %u", ie[4]); + + switch (ie[4]) { + case MEASURE_TYPE_LCI: + hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]); + break; + default: + wpa_printf(MSG_DEBUG, + "Measurement report type %u is not supported", + ie[4]); + } + + len -= ie - pos + ie[1] + 2; + pos = ie + ie[1] + 2; + } +} static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len) @@ -225,6 +289,9 @@ void hostapd_handle_radio_measurement(struct hostapd_data *hapd, mgmt->u.action.u.rrm.action); switch (mgmt->u.action.u.rrm.action) { + case WLAN_RRM_RADIO_MEASUREMENT_REPORT: + hostapd_handle_radio_msmt_report(hapd, buf, len); + break; case WLAN_RRM_NEIGHBOR_REPORT_REQUEST: hostapd_handle_nei_report_req(hapd, buf, len); break; @@ -233,3 +300,94 @@ void hostapd_handle_radio_measurement(struct hostapd_data *hapd, mgmt->u.action.u.rrm.action); } } + + +int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr) +{ + struct wpabuf *buf; + struct sta_info *sta = ap_get_sta(hapd, addr); + int ret; + + if (!sta) { + wpa_printf(MSG_ERROR, + "Request LCI: destination address isn't in station list"); + return -1; + } + + if (!(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_ERROR, + "Request LCI: destination address isn't connected"); + return -1; + } + + if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) { + wpa_printf(MSG_ERROR, + "Request LCI: station doesn't support LCI in RRM"); + return -1; + } + + if (hapd->lci_req_active) { + wpa_printf(MSG_DEBUG, + "Request LCI: LCI request is already in process, overriding."); + hapd->lci_req_active = 0; + eloop_cancel_timeout(hoastapd_lci_rep_timeout_handler, hapd, + NULL); + } + + /* Measurement request (5) + Measurement IE with LCI (10) */ + buf = wpabuf_alloc(5 + 10); + if (!buf) + return -1; + + hapd->lci_req_token++; + /* For wraparounds - the token must be non zero */ + if (!hapd->lci_req_token) + hapd->lci_req_token++; + + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); + wpabuf_put_u8(buf, hapd->lci_req_token); + /* Num of repetiotions */ + wpabuf_put_le16(buf, 0); + + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, 3 + 1 + 4); + + /* Measurement token */ + wpabuf_put_u8(buf, 1); + /* Parallel and enable bits are 0, duration, request and report are + * reserved + */ + wpabuf_put_u8(buf, 0); + wpabuf_put_u8(buf, MEASURE_TYPE_LCI); + + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + + wpabuf_put_u8(buf, LCI_REQ_SUBELEMENT_MAX_AGE); + wpabuf_put_u8(buf, 2); + wpabuf_put_le16(buf, 0xffff); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + if (ret) + return ret; + + hapd->lci_req_active = 1; + + eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, + hoastapd_lci_rep_timeout_handler, + hapd, NULL); + + return 0; +} + + +void hostapd_clean_rrm(struct hostapd_data *hapd) +{ + hostpad_free_neighbor_db(hapd); + eloop_cancel_timeout(hoastapd_lci_rep_timeout_handler, hapd, + NULL); + hapd->lci_req_active = 0; +} diff --git a/src/ap/rrm.h b/src/ap/rrm.h index 3df49ab..08ae438 100644 --- a/src/ap/rrm.h +++ b/src/ap/rrm.h @@ -13,4 +13,7 @@ void hostapd_handle_radio_measurement(struct hostapd_data *hapd, const u8 *buf, size_t len); +int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr); +void hostapd_clean_rrm(struct hostapd_data *hapd); + #endif /* RRM_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 3a0ef21..5f548dd 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -366,6 +366,8 @@ /* byte 1 (out of 5) */ #define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0) #define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1) +/* byte 2 (out of 5) */ +#define WLAN_RRM_CAPS_LCI_MEASUREMENT BIT(4) /* Timeout Interval Type */ #define WLAN_TIMEOUT_REASSOC_DEADLINE 1 -- 1.9.1 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap