From f4f185a224ffde55802ce33a856dd3ddf929dfcf Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Wed, 6 Apr 2016 19:42:11 +0300 Subject: [PATCH] hostapd: Add LCI request Add a hostapd control interface command REQ_LCI to request LCI from an associated station using radio measurement. Signed-off-by: David Spinadel --- hostapd/ctrl_iface.c | 18 +++++ hostapd/hostapd_cli.c | 21 ++++++ src/ap/hostapd.c | 3 +- src/ap/hostapd.h | 3 + src/ap/rrm.c | 154 +++++++++++++++++++++++++++++++++++++++++++ src/ap/rrm.h | 2 + src/common/ieee802_11_defs.h | 2 + 7 files changed, 202 insertions(+), 1 deletion(-) diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index dd115df..4c2b559 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -48,6 +48,7 @@ #include "ap/wpa_auth.h" #include "ap/beacon.h" #include "ap/neighbor_db.h" +#include "ap/rrm.h" #include "wps/wps_defs.h" #include "wps/wps.h" #include "fst/fst_ctrl_iface.h" @@ -2072,6 +2073,20 @@ 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_INFO, "CTRL: REQ_LCI: Invalid 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; @@ -2439,6 +2454,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 ec91f7e..563d8d5 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1186,6 +1186,26 @@ static int hostapd_cli_cmd_remove_neighbor(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_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); +} + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); @@ -1249,6 +1269,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_cmd_req_lci }, { NULL, NULL } }; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 336cfca..42c1aaa 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 a06b727..76b0ca6 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; + unsigned int lci_req_active:1; }; diff --git a/src/ap/rrm.c b/src/ap/rrm.c index 92985dc..8548ea4 100644 --- a/src/ap/rrm.c +++ b/src/ap/rrm.c @@ -12,8 +12,72 @@ #include "utils/common.h" #include "hostapd.h" #include "ap_drv_ops.h" +#include "sta_info.h" +#include "eloop.h" +#include "neighbor_db.h" #include "rrm.h" +#define HOSTAPD_RRM_REQUEST_TIMEOUT 5 + + +static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_data; + + wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) 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 %u", token); + return; + } + + hapd->lci_req_active = 0; + eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL); + wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len); +} + + +static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd, + const u8 *buf, size_t len) +{ + const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; + const u8 *pos, *ie, *end; + u8 token; + + end = buf + len; + token = mgmt->u.action.u.rrm.dialog_token; + pos = mgmt->u.action.u.rrm.variable; + + while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) { + if (ie[1] < 5) { + wpa_printf(MSG_DEBUG, "Bad Measurement Report element"); + 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]); + break; + } + + pos = ie + ie[1] + 2; + } +} + static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len) { @@ -229,6 +293,9 @@ void hostapd_handle_radio_measurement(struct hostapd_data *hapd, mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa)); 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; @@ -238,3 +305,90 @@ void hostapd_handle_radio_measurement(struct hostapd_data *hapd, break; } } + + +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_INFO, + "Request LCI: Destination address is not in station list"); + return -1; + } + + if (!(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_INFO, + "Request LCI: Destination address is not connected"); + return -1; + } + + if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) { + wpa_printf(MSG_INFO, + "Request LCI: Station does not 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(hostapd_lci_rep_timeout_handler, hapd, + NULL); + } + + /* Measurement request (5) + Measurement element with LCI (10) */ + buf = wpabuf_alloc(5 + 10); + if (!buf) + return -1; + + hapd->lci_req_token++; + /* For wraparounds - the token must be nonzero */ + 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); + wpabuf_put_le16(buf, 0); /* Number of repetitions */ + + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, 3 + 1 + 4); + + wpabuf_put_u8(buf, 1); /* Measurement Token */ + /* + * 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_SUBELEM_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, + hostapd_lci_rep_timeout_handler, hapd, NULL); + + return 0; +} + + +void hostapd_clean_rrm(struct hostapd_data *hapd) +{ + hostpad_free_neighbor_db(hapd); + eloop_cancel_timeout(hostapd_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..f3e15bd 100644 --- a/src/ap/rrm.h +++ b/src/ap/rrm.h @@ -12,5 +12,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 a713764..cbf8336 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 -- 2.1.4