diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 56b2df3ae..7a32c22f3 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -4662,6 +4662,10 @@ static int hostapd_config_fill(struct hostapd_config *conf, WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE | WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE | 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) { bss->gas_address3 = atoi(pos); } else if (os_strcmp(buf, "stationary_ap") == 0) { diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 23cc52a28..9fb6010c7 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -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, 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) { reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11, 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) { reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply, reply_size); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index d80abcac0..e34d75c86 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -3112,6 +3112,9 @@ own_ip_addr=127.0.0.1 # Enable neighbor report via radio measurements #rrm_neighbor_report=1 +# Enable link measurement report via radio measurements +#rrm_link_measurement_report=1 + # Enable beacon report via radio measurements #rrm_beacon_report=1 diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index a9d326de8..e1fe2860b 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -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, char *argv[]) { @@ -1847,6 +1854,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { " = poll a STA to check connectivity with a QoS null frame" }, { "req_beacon", hostapd_cli_cmd_req_beacon, NULL, " [req_mode=] = send a Beacon report request to a station" }, + { "req_link_measurement", hostapd_cli_cmd_req_link_measurement, NULL, + " = send a link measurement report request to a station"}, { "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL, "= reload wpa_psk_file only" }, #ifdef CONFIG_IEEE80211R_AP diff --git a/hostapd/main.c b/hostapd/main.c index a43d3a5be..8b9b96a69 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -309,6 +309,7 @@ setup_mld: iface->drv_flags = capa.flags; iface->drv_flags2 = capa.flags2; + iface->drv_rrm_flags = capa.rrm_flags; iface->probe_resp_offloads = capa.probe_resp_offloads; /* * Use default extended capa values from per-radio information diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 85f8975fa..4797d7d93 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -405,8 +405,10 @@ struct hostapd_data { u8 beacon_req_token; u8 lci_req_token; u8 range_req_token; + u8 link_measurement_req_token; unsigned int lci_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 */ @@ -577,6 +579,7 @@ struct hostapd_iface { u64 drv_flags; u64 drv_flags2; + unsigned int drv_rrm_flags; /* * A bitmap of supported protocols for probe response offload. See diff --git a/src/ap/rrm.c b/src/ap/rrm.c index f2d5cd16e..fbcddf3f9 100644 --- a/src/ap/rrm.c +++ b/src/ap/rrm.c @@ -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, 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: hostapd_handle_nei_report_req(hapd, buf, len); break; + case WLAN_RRM_LINK_MEASUREMENT_REPORT: + hostapd_handle_link_mesr_report(hapd, buf, len); + break; default: wpa_printf(MSG_DEBUG, "RRM action %u is not supported", mgmt->u.action.u.rrm.action); @@ -563,6 +613,7 @@ void hostapd_clean_rrm(struct hostapd_data *hapd) hapd->lci_req_active = 0; eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); 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), 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; +} diff --git a/src/ap/rrm.h b/src/ap/rrm.h index 02cd522ee..17751e02c 100644 --- a/src/ap/rrm.h +++ b/src/ap/rrm.h @@ -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, const struct ieee80211_mgmt *mgmt, size_t len, int ok); +int hostapd_send_link_measurement_req(struct hostapd_data *hapd, + const u8 *addr); #endif /* RRM_H */ diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index c5bb9abd7..f6142501e 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -413,6 +413,9 @@ extern "C" { /* parameters: */ #define BEACON_RESP_RX "BEACON-RESP-RX " +/* parameters: */ +#define LINK_MSR_RESP_RX "LINK-MSR-RESP-RX " + /* PMKSA cache entry added; parameters: */ #define PMKSA_CACHE_ADDED "PMKSA-CACHE-ADDED " /* PMKSA cache entry removed; parameters: */