hostapd: Add RRM link measurement request support

RRM link measurement request/report management frames are used to get
the radio link information between the connected stations.

Add new hostapd_cli command req_link_measurement to send an RRM link
measurement request to an associated station. Add support to handle the
link measurement report in hostapd.

RRM link measurement support can be enabled with the following new
configuration parameter:
rrm_link_measurement_report=1

Signed-off-by: Raj Kumar Bhagat <quic_rajkbhag@quicinc.com>
Signed-off-by: Yuvarani V <quic_yuvarani@quicinc.com>
This commit is contained in:
Raj Kumar Bhagat 2024-04-04 11:58:09 +05:30 committed by Jouni Malinen
parent 92fdb49b2e
commit 1be706e862
9 changed files with 169 additions and 0 deletions

View file

@ -4662,6 +4662,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE | WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE | WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
WLAN_RRM_CAPS_BEACON_REPORT_TABLE; WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
} else if (os_strcmp(buf, "rrm_link_measurement_report") == 0) {
if (atoi(pos))
bss->radio_measurements[0] |=
WLAN_RRM_CAPS_LINK_MEASUREMENT;
} else if (os_strcmp(buf, "gas_address3") == 0) { } else if (os_strcmp(buf, "gas_address3") == 0) {
bss->gas_address3 = atoi(pos); bss->gas_address3 = atoi(pos);
} else if (os_strcmp(buf, "stationary_ap") == 0) { } else if (os_strcmp(buf, "stationary_ap") == 0) {

View file

@ -3207,6 +3207,26 @@ static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd,
} }
static int hostapd_ctrl_iface_req_link_measurement(struct hostapd_data *hapd,
const char *cmd, char *reply,
size_t reply_size)
{
u8 addr[ETH_ALEN];
int ret;
if (hwaddr_aton(cmd, addr)) {
wpa_printf(MSG_ERROR,
"CTRL: REQ_LINK_MEASUREMENT: Invalid MAC address");
return -1;
}
ret = hostapd_send_link_measurement_req(hapd, addr);
if (ret >= 0)
ret = os_snprintf(reply, reply_size, "%d", ret);
return ret;
}
static int hostapd_ctrl_iface_show_neighbor(struct hostapd_data *hapd, static int hostapd_ctrl_iface_show_neighbor(struct hostapd_data *hapd,
char *buf, size_t buflen) char *buf, size_t buflen)
{ {
@ -4185,6 +4205,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) { } else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) {
reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11, reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11,
reply, reply_size); reply, reply_size);
} else if (os_strncmp(buf, "REQ_LINK_MEASUREMENT ", 21) == 0) {
reply_len = hostapd_ctrl_iface_req_link_measurement(
hapd, buf + 21, reply, reply_size);
} else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) { } else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply, reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply,
reply_size); reply_size);

View file

@ -3112,6 +3112,9 @@ own_ip_addr=127.0.0.1
# Enable neighbor report via radio measurements # Enable neighbor report via radio measurements
#rrm_neighbor_report=1 #rrm_neighbor_report=1
# Enable link measurement report via radio measurements
#rrm_link_measurement_report=1
# Enable beacon report via radio measurements # Enable beacon report via radio measurements
#rrm_beacon_report=1 #rrm_beacon_report=1

View file

@ -1605,6 +1605,13 @@ static int hostapd_cli_cmd_req_beacon(struct wpa_ctrl *ctrl, int argc,
} }
static int hostapd_cli_cmd_req_link_measurement(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "REQ_LINK_MEASUREMENT", 1, argc, argv);
}
static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc, static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc,
char *argv[]) char *argv[])
{ {
@ -1847,6 +1854,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
"<addr> = poll a STA to check connectivity with a QoS null frame" }, "<addr> = poll a STA to check connectivity with a QoS null frame" },
{ "req_beacon", hostapd_cli_cmd_req_beacon, NULL, { "req_beacon", hostapd_cli_cmd_req_beacon, NULL,
"<addr> [req_mode=] <measurement request hexdump> = send a Beacon report request to a station" }, "<addr> [req_mode=] <measurement request hexdump> = send a Beacon report request to a station" },
{ "req_link_measurement", hostapd_cli_cmd_req_link_measurement, NULL,
"<addr> = send a link measurement report request to a station"},
{ "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL, { "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL,
"= reload wpa_psk_file only" }, "= reload wpa_psk_file only" },
#ifdef CONFIG_IEEE80211R_AP #ifdef CONFIG_IEEE80211R_AP

View file

@ -309,6 +309,7 @@ setup_mld:
iface->drv_flags = capa.flags; iface->drv_flags = capa.flags;
iface->drv_flags2 = capa.flags2; iface->drv_flags2 = capa.flags2;
iface->drv_rrm_flags = capa.rrm_flags;
iface->probe_resp_offloads = capa.probe_resp_offloads; iface->probe_resp_offloads = capa.probe_resp_offloads;
/* /*
* Use default extended capa values from per-radio information * Use default extended capa values from per-radio information

View file

@ -405,8 +405,10 @@ struct hostapd_data {
u8 beacon_req_token; u8 beacon_req_token;
u8 lci_req_token; u8 lci_req_token;
u8 range_req_token; u8 range_req_token;
u8 link_measurement_req_token;
unsigned int lci_req_active:1; unsigned int lci_req_active:1;
unsigned int range_req_active:1; unsigned int range_req_active:1;
unsigned int link_mesr_req_active:1;
int dhcp_sock; /* UDP socket used with the DHCP server */ int dhcp_sock; /* UDP socket used with the DHCP server */
@ -577,6 +579,7 @@ struct hostapd_iface {
u64 drv_flags; u64 drv_flags;
u64 drv_flags2; u64 drv_flags2;
unsigned int drv_rrm_flags;
/* /*
* A bitmap of supported protocols for probe response offload. See * A bitmap of supported protocols for probe response offload. See

View file

@ -334,6 +334,53 @@ static void hostapd_handle_nei_report_req(struct hostapd_data *hapd,
} }
static void hostapd_link_mesr_rep_timeout_handler(void *eloop_data,
void *user_ctx)
{
struct hostapd_data *hapd = eloop_data;
wpa_printf(MSG_DEBUG,
"RRM: Link measurement request (token %u) timed out",
hapd->link_measurement_req_token);
hapd->link_mesr_req_active = 0;
}
static void hostapd_handle_link_mesr_report(struct hostapd_data *hapd,
const u8 *buf, size_t len)
{
const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
const struct rrm_link_measurement_report *report;
const u8 *pos, *end;
char report_msg[2 * 8 + 1];
end = buf + len;
pos = mgmt->u.action.u.rrm.variable;
report = (const struct rrm_link_measurement_report *) (pos - 1);
if (end - (const u8 *) report < (int) sizeof(*report))
return;
if (!hapd->link_mesr_req_active ||
(hapd->link_measurement_req_token != report->dialog_token)) {
wpa_printf(MSG_INFO,
"Unexpected Link measurement report, token %u",
report->dialog_token);
return;
}
hapd->link_mesr_req_active = 0;
eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler, hapd, NULL);
report_msg[0] = '\0';
if (wpa_snprintf_hex(report_msg, sizeof(report_msg),
pos, end - pos) < 0)
return;
wpa_msg(hapd->msg_ctx, MSG_INFO, LINK_MSR_RESP_RX MACSTR " %u %s",
MAC2STR(mgmt->sa), report->dialog_token, report_msg);
}
void hostapd_handle_radio_measurement(struct hostapd_data *hapd, void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
const u8 *buf, size_t len) const u8 *buf, size_t len)
{ {
@ -356,6 +403,9 @@ void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
case WLAN_RRM_NEIGHBOR_REPORT_REQUEST: case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
hostapd_handle_nei_report_req(hapd, buf, len); hostapd_handle_nei_report_req(hapd, buf, len);
break; break;
case WLAN_RRM_LINK_MEASUREMENT_REPORT:
hostapd_handle_link_mesr_report(hapd, buf, len);
break;
default: default:
wpa_printf(MSG_DEBUG, "RRM action %u is not supported", wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
mgmt->u.action.u.rrm.action); mgmt->u.action.u.rrm.action);
@ -563,6 +613,7 @@ void hostapd_clean_rrm(struct hostapd_data *hapd)
hapd->lci_req_active = 0; hapd->lci_req_active = 0;
eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
hapd->range_req_active = 0; hapd->range_req_active = 0;
eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler, hapd, NULL);
} }
@ -672,3 +723,73 @@ void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
" %u ack=%d", MAC2STR(mgmt->da), " %u ack=%d", MAC2STR(mgmt->da),
mgmt->u.action.u.rrm.dialog_token, ok); mgmt->u.action.u.rrm.dialog_token, ok);
} }
int hostapd_send_link_measurement_req(struct hostapd_data *hapd, const u8 *addr)
{
struct wpabuf *buf;
struct sta_info *sta;
int ret;
wpa_printf(MSG_DEBUG, "Request Link Measurement: dest addr " MACSTR,
MAC2STR(addr));
if (!(hapd->iface->drv_rrm_flags &
WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
wpa_printf(MSG_INFO,
"Request Link Measurement: the driver does not support TX power insertion");
return -1;
}
sta = ap_get_sta(hapd, addr);
if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
wpa_printf(MSG_INFO,
"Request Link Measurement: specied STA is not connected");
return -1;
}
if (!(sta->rrm_enabled_capa[0] & WLAN_RRM_CAPS_LINK_MEASUREMENT)) {
wpa_printf(MSG_INFO,
"Request Link Measurement: destination STA does not support link measurement");
return -1;
}
if (hapd->link_mesr_req_active) {
wpa_printf(MSG_DEBUG,
"Request Link Measurement: request already in process - overriding");
hapd->link_mesr_req_active = 0;
eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler,
hapd, NULL);
}
/* Action + Action type + token + Tx Power used + Max Tx Power = 5 */
buf = wpabuf_alloc(5);
if (!buf)
return -1;
hapd->link_measurement_req_token++;
if (!hapd->link_measurement_req_token)
hapd->link_measurement_req_token++;
wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REQUEST);
wpabuf_put_u8(buf, hapd->link_measurement_req_token);
/* NOTE: The driver is expected to fill the Tx Power Used and Max Tx
* Power */
wpabuf_put_u8(buf, 0);
wpabuf_put_u8(buf, 0);
ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
wpabuf_head(buf), wpabuf_len(buf));
wpabuf_free(buf);
if (ret < 0)
return ret;
hapd->link_mesr_req_active = 1;
eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
hostapd_link_mesr_rep_timeout_handler, hapd,
NULL);
return hapd->link_measurement_req_token;
}

View file

@ -29,5 +29,7 @@ int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd, void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, const struct ieee80211_mgmt *mgmt,
size_t len, int ok); size_t len, int ok);
int hostapd_send_link_measurement_req(struct hostapd_data *hapd,
const u8 *addr);
#endif /* RRM_H */ #endif /* RRM_H */

View file

@ -413,6 +413,9 @@ extern "C" {
/* parameters: <STA address> <dialog token> <report mode> <beacon report> */ /* parameters: <STA address> <dialog token> <report mode> <beacon report> */
#define BEACON_RESP_RX "BEACON-RESP-RX " #define BEACON_RESP_RX "BEACON-RESP-RX "
/* parameters: <STA address> <dialog token> <link measurement report> */
#define LINK_MSR_RESP_RX "LINK-MSR-RESP-RX "
/* PMKSA cache entry added; parameters: <BSSID> <network_id> */ /* PMKSA cache entry added; parameters: <BSSID> <network_id> */
#define PMKSA_CACHE_ADDED "PMKSA-CACHE-ADDED " #define PMKSA_CACHE_ADDED "PMKSA-CACHE-ADDED "
/* PMKSA cache entry removed; parameters: <BSSID> <network_id> */ /* PMKSA cache entry removed; parameters: <BSSID> <network_id> */