diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 64d02100c..6b4aa0542 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -2236,6 +2236,46 @@ static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd) } +static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd, + const char *cmd, char *reply, + size_t reply_size) +{ + u8 addr[ETH_ALEN]; + const char *pos; + struct wpabuf *req; + int ret; + u8 req_mode = 0; + + if (hwaddr_aton(cmd, addr)) + return -1; + pos = os_strchr(cmd, ' '); + if (!pos) + return -1; + pos++; + if (os_strncmp(pos, "req_mode=", 9) == 0) { + int val = hex2byte(pos + 9); + + if (val < 0) + return -1; + req_mode = val; + pos += 11; + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + } + req = wpabuf_parse_bin(pos); + if (!req) + return -1; + + ret = hostapd_send_beacon_req(hapd, addr, req_mode, req); + wpabuf_free(req); + if (ret >= 0) + ret = os_snprintf(reply, reply_size, "%d", ret); + return ret; +} + + static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf) { struct wpa_ssid_value ssid; @@ -2653,6 +2693,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) { if (hostapd_ctrl_iface_req_range(hapd, buf + 10)) reply_len = -1; + } 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_strcmp(buf, "DRIVER_FLAGS") == 0) { reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply, reply_size); diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 45176edb9..bc0ac23b0 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -298,6 +298,7 @@ struct hostapd_data { struct dl_list nr_db; + u8 beacon_req_token; u8 lci_req_token; u8 range_req_token; unsigned int lci_req_active:1; diff --git a/src/ap/rrm.c b/src/ap/rrm.c index 5ca87c0be..78557dc68 100644 --- a/src/ap/rrm.c +++ b/src/ap/rrm.c @@ -542,3 +542,99 @@ void hostapd_clean_rrm(struct hostapd_data *hapd) eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); hapd->range_req_active = 0; } + + +int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr, + u8 req_mode, const struct wpabuf *req) +{ + struct wpabuf *buf; + struct sta_info *sta = ap_get_sta(hapd, addr); + int ret; + enum beacon_report_mode mode; + const u8 *pos; + + /* Request data: + * Operating Class (1), Channel Number (1), Randomization Interval (2), + * Measurement Duration (2), Measurement Mode (1), BSSID (6), + * Optional Subelements (variable) + */ + if (wpabuf_len(req) < 13) { + wpa_printf(MSG_INFO, "Beacon request: Too short request data"); + return -1; + } + pos = wpabuf_head(req); + mode = pos[6]; + + if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR " is not connected", + MAC2STR(addr)); + return -1; + } + + switch (mode) { + case BEACON_REPORT_MODE_PASSIVE: + if (!(sta->rrm_enabled_capa[0] & + WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR + " does not support passive beacon report", + MAC2STR(addr)); + return -1; + } + break; + case BEACON_REPORT_MODE_ACTIVE: + if (!(sta->rrm_enabled_capa[0] & + WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR + " does not support active beacon report", + MAC2STR(addr)); + return -1; + } + break; + case BEACON_REPORT_MODE_TABLE: + if (!(sta->rrm_enabled_capa[0] & + WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR + " does not support table beacon report", + MAC2STR(addr)); + return -1; + } + break; + default: + wpa_printf(MSG_INFO, + "Beacon request: Unknown measurement mode %d", mode); + return -1; + } + + buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req)); + if (!buf) + return -1; + + hapd->beacon_req_token++; + if (!hapd->beacon_req_token) + hapd->beacon_req_token++; + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); + wpabuf_put_u8(buf, hapd->beacon_req_token); + wpabuf_put_le16(buf, 0); /* Number of repetitions */ + + /* Measurement Request element */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, 3 + wpabuf_len(req)); + wpabuf_put_u8(buf, 1); /* Measurement Token */ + wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */ + wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */ + wpabuf_put_buf(buf, req); + + 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; + + return hapd->beacon_req_token; +} diff --git a/src/ap/rrm.h b/src/ap/rrm.h index f07fd41ac..720fad9b1 100644 --- a/src/ap/rrm.h +++ b/src/ap/rrm.h @@ -24,5 +24,7 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, u16 random_interval, u8 min_ap, const u8 *responders, unsigned int n_responders); void hostapd_clean_rrm(struct hostapd_data *hapd); +int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr, + u8 req_mode, const struct wpabuf *req); #endif /* RRM_H */