From: David Spinadel <david.spinadel@xxxxxxxxx> Handle neighbor report request and send neighbor report response, based on configured neighbor report data. Signed-off-by: David Spinadel <david.spinadel@xxxxxxxxx> --- hostapd/Android.mk | 1 + hostapd/Makefile | 1 + src/ap/ieee802_11.c | 4 + src/ap/rrm.c | 235 +++++++++++++++++++++++++++++++++++++++++++ src/ap/rrm.h | 16 +++ src/common/ieee802_11_defs.h | 7 ++ wpa_supplicant/Android.mk | 1 + wpa_supplicant/Makefile | 1 + 8 files changed, 266 insertions(+) create mode 100644 src/ap/rrm.c create mode 100644 src/ap/rrm.h diff --git a/hostapd/Android.mk b/hostapd/Android.mk index 42f67bc..c7467fd 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -97,6 +97,7 @@ OBJS += src/ap/ieee802_11_shared.c OBJS += src/ap/beacon.c OBJS += src/ap/bss_load.c OBJS += src/ap/neighbor_db.c +OBJS += src/ap/rrm.c OBJS_d = OBJS_p = LIBS = diff --git a/hostapd/Makefile b/hostapd/Makefile index 35b055c..baa7819 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -85,6 +85,7 @@ OBJS += ../src/ap/ieee802_11_shared.o OBJS += ../src/ap/beacon.o OBJS += ../src/ap/bss_load.o OBJS += ../src/ap/neighbor_db.o +OBJS += ../src/ap/rrm.o OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index b1ce56d..4f65cf3 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -43,6 +43,7 @@ #include "ieee802_11.h" #include "dfs.h" #include "mbo_ap.h" +#include "rrm.h" u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) @@ -2472,6 +2473,9 @@ static int handle_action(struct hostapd_data *hapd, return 1; } break; + case WLAN_ACTION_RADIO_MEASUREMENT: + hostapd_handle_radio_measurement(hapd, (u8 *)mgmt, len); + return 1; } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, diff --git a/src/ap/rrm.c b/src/ap/rrm.c new file mode 100644 index 0000000..7df25ae --- /dev/null +++ b/src/ap/rrm.c @@ -0,0 +1,235 @@ +/* + * hostapd / Radio Measurement (RRM) + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include "utils/common.h" +#include "hostapd.h" +#include "ap_drv_ops.h" + + +static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len) +{ + const u8 *subelem; + + /* Range req IE + Location subject + max age subelement */ + if (len < 3 + 1 + 4) + return 0; + + /* Subelements are arranged as IEs */ + subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEMENT_MAX_AGE); + if (subelem && subelem[1] == 2) + return *(u16 *)(subelem + 2); + + return 0; +} + +static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age) +{ + struct os_time curr, diff; + unsigned long diff_l; + + if (!max_age) + return 0; + + if (max_age == 0xffff) + return 1; + + if (os_get_time(&curr)) + return 0; + + os_time_sub(&curr, &nr->lci_date, &diff); + + /* avoid overflow */ + if (diff.sec > 0xffff) + return 0; + + /* LCI age is calculated in 10th of a second units. */ + diff_l = diff.sec * 10 + diff.usec / 100000; + + return max_age > diff_l; +} + + +static size_t hostapd_neighbor_report_len(struct wpabuf *buf, + struct hostapd_neighbor_entry *nr, + int send_lci, int send_civic) +{ + size_t len = 2 + wpabuf_len(nr->nr); + + if (send_lci && nr->lci) + len += 2 + wpabuf_len(nr->lci); + + if (send_civic && nr->civic) + len += 2 + wpabuf_len(nr->civic); + + return len; +} + + +static void hostapd_send_nei_report_resp(struct hostapd_data *hapd, + const u8 *addr, u8 dialog_token, + struct wpa_ssid_value *ssid, u8 lci, + u8 civic, u16 lci_max_age) +{ + struct hostapd_neighbor_entry *nr; + struct wpabuf *buf; + u8 *msmt_token; + + /* The number and length of the Neighbor Report Elements in a Neighbor + * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes + * of RRM header + */ + buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE); + if (!buf) + return; + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE); + wpabuf_put_u8(buf, dialog_token); + + dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, + list) { + int send_lci; + size_t len; + + if (ssid->ssid_len != nr->ssid.ssid_len || + os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len)) + continue; + + send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age); + len = hostapd_neighbor_report_len(buf, nr, send_lci, civic); + + if (len - 2 > 0xff) { + wpa_printf(MSG_DEBUG, + "NR entry for " MACSTR " exceeds 0xFF bytes", + MAC2STR(nr->bssid)); + continue; + } + + if (len > wpabuf_tailroom(buf)) + break; + + wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT); + wpabuf_put_u8(buf, len - 2); + wpabuf_put_buf(buf, nr->nr); + + if (send_lci && nr->lci) { + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT); + wpabuf_put_u8(buf, wpabuf_len(nr->lci)); + /* overriding measurement token - the first byte of the + * measurement IE + */ + msmt_token = wpabuf_mhead_u8(buf) + wpabuf_len(buf); + wpabuf_put_buf(buf, nr->lci); + *msmt_token = lci; + } + + if (civic && nr->civic) { + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT); + wpabuf_put_u8(buf, wpabuf_len(nr->civic)); + /* overriding measurement token - the first byte of the + * measurement IE + */ + msmt_token = wpabuf_mhead_u8(buf) + wpabuf_len(buf); + wpabuf_put_buf(buf, nr->civic); + *msmt_token = civic; + } + } + + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); +} + + +static void hostapd_handle_nei_report_req(struct hostapd_data *hapd, + const u8 *buf, size_t len) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + const u8 *pos, *ie, *end; + struct wpa_ssid_value ssid = { + .ssid_len = 0 + }; + u8 token; + u8 lci = 0, civic = 0; /* Measurement tokens */ + u16 lci_max_age = 0; + + if (!(hapd->conf->radio_measurements[0] & + WLAN_RRM_CAPS_NEIGHBOR_REPORT)) + return; + + end = buf + len; + + token = mgmt->u.action.u.rrm.dialog_token; + pos = mgmt->u.action.u.rrm.variable; + len = end - pos; + + ie = get_ie(pos, len, WLAN_EID_SSID); + if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) { + ssid.ssid_len = ie[1]; + os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len); + } else { + ssid.ssid_len = hapd->conf->ssid.ssid_len; + os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); + } + + while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) { + if (ie[1] < 3) + break; + + wpa_printf(MSG_DEBUG, + "Neighbor report request, measure type %u", + ie[4]); + + switch (ie[4]) { /* Measurement type */ + case MEASURE_TYPE_LCI: + lci = ie[2]; /* Measurement token */ + lci_max_age = + hostapd_parse_location_lci_req_age(ie + 2, + ie[1]); + break; + case MEASURE_TYPE_LOCATION_CIVIC: + civic = ie[2]; /* Measurement token */ + break; + default: + break; + } + + pos = ie + ie[1] + 2; + len = end - pos; + } + + hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic, + lci_max_age); +} + + +void hostapd_handle_radio_measurement(struct hostapd_data *hapd, + const u8 *buf, size_t len) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + + /* check for enough bytes: header + (1B)category + (1B)action + + * (1B)dialog_token. + */ + if (len < IEEE80211_HDRLEN + 3) + return; + + wpa_printf(MSG_INFO, "Radio measurement frame, action %d", + mgmt->u.action.u.rrm.action); + + switch (mgmt->u.action.u.rrm.action) { + case WLAN_RRM_NEIGHBOR_REPORT_REQUEST: + hostapd_handle_nei_report_req(hapd, buf, len); + break; + default: + wpa_printf(MSG_DEBUG, "RRM action %d is not supported", + mgmt->u.action.u.rrm.action); + } +} diff --git a/src/ap/rrm.h b/src/ap/rrm.h new file mode 100644 index 0000000..3df49ab --- /dev/null +++ b/src/ap/rrm.h @@ -0,0 +1,16 @@ +/* + * hostapd / Radio Measurement (RRM) + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RRM_H +#define RRM_H + +void hostapd_handle_radio_measurement(struct hostapd_data *hapd, + const u8 *buf, size_t len); + +#endif /* RRM_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 5b410bf..3a0ef21 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -674,12 +674,19 @@ struct ieee80211_mgmt { u8 action; u8 variable[]; } STRUCT_PACKED fst_action; + struct { + u8 action; + u8 dialog_token; + u8 variable[]; + } STRUCT_PACKED rrm; } u; } STRUCT_PACKED action; } u; } STRUCT_PACKED; +#define IEEE80211_MAX_MMPDU_SIZE 2304 + /* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */ #define IEEE80211_HT_MCS_MASK_LEN 10 diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index d1adda5..0e08152 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -798,6 +798,7 @@ OBJS += src/ap/beacon.c OBJS += src/ap/bss_load.c OBJS += src/ap/eap_user_db.c OBJS += src/ap/neighbor_db.c +OBJS += src/ap/rrm.c ifdef CONFIG_IEEE80211N OBJS += src/ap/ieee802_11_ht.c ifdef CONFIG_IEEE80211AC diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 06c26d2..700c67a 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -845,6 +845,7 @@ OBJS += ../src/ap/beacon.o OBJS += ../src/ap/bss_load.o OBJS += ../src/ap/eap_user_db.o OBJS += ../src/ap/neighbor_db.o +OBJS += ../src/ap/rrm.o ifdef CONFIG_IEEE80211N OBJS += ../src/ap/ieee802_11_ht.o ifdef CONFIG_IEEE80211AC -- 1.9.1 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap