From 79216243d75ddfeab9c71eac9059b302bc7f6d8b Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 4 Jul 2017 14:45:10 +0200 Subject: [PATCH] hostapd: add support for accessing 802.11k neighbor report elements via ubus This API can be used to distribute neighbor report entries across multiple APs on the same LAN. Signed-off-by: Felix Fietkau --- .../services/hostapd/src/src/ap/ubus.c | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/package/network/services/hostapd/src/src/ap/ubus.c b/package/network/services/hostapd/src/src/ap/ubus.c index 715c456049..bc773930d4 100644 --- a/package/network/services/hostapd/src/src/ap/ubus.c +++ b/package/network/services/hostapd/src/src/ap/ubus.c @@ -9,8 +9,10 @@ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" +#include "utils/wpabuf.h" #include "common/ieee802_11_defs.h" #include "hostapd.h" +#include "neighbor_db.h" #include "wps_hostapd.h" #include "sta_info.h" #include "ubus.h" @@ -447,6 +449,170 @@ hostapd_vendor_elements(struct ubus_context *ctx, struct ubus_object *obj, return UBUS_STATUS_OK; } +static void +hostapd_rrm_print_nr(struct hostapd_neighbor_entry *nr) +{ + const u8 *data; + char *str; + int len; + + blobmsg_printf(&b, "", MACSTR, MAC2STR(nr->bssid)); + + str = blobmsg_alloc_string_buffer(&b, "", nr->ssid.ssid_len + 1); + memcpy(str, nr->ssid.ssid, nr->ssid.ssid_len); + str[nr->ssid.ssid_len] = 0; + blobmsg_add_string_buffer(&b); + + len = wpabuf_len(nr->nr); + str = blobmsg_alloc_string_buffer(&b, "", 2 * len + 1); + wpa_snprintf_hex(str, 2 * len + 1, wpabuf_head_u8(nr->nr), len); + blobmsg_add_string_buffer(&b); +} + +static int +hostapd_rrm_nr_get_own(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct hostapd_data *hapd = get_hapd_from_object(obj); + struct hostapd_neighbor_entry *nr; + void *c; + + if (!(hapd->conf->radio_measurements[0] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) + return UBUS_STATUS_NOT_SUPPORTED; + + nr = hostapd_neighbor_get(hapd, hapd->own_addr, NULL); + if (!nr) + return UBUS_STATUS_NOT_FOUND; + + blob_buf_init(&b, 0); + + c = blobmsg_open_array(&b, "value"); + hostapd_rrm_print_nr(nr); + blobmsg_close_array(&b, c); + + ubus_send_reply(ctx, req, b.head); + + return 0; +} + +static int +hostapd_rrm_nr_list(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct hostapd_data *hapd = get_hapd_from_object(obj); + struct hostapd_neighbor_entry *nr; + void *c; + + if (!(hapd->conf->radio_measurements[0] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) + return UBUS_STATUS_NOT_SUPPORTED; + + blob_buf_init(&b, 0); + + c = blobmsg_open_array(&b, "list"); + dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) { + void *cur; + + if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN)) + continue; + + cur = blobmsg_open_array(&b, NULL); + hostapd_rrm_print_nr(nr); + blobmsg_close_array(&b, cur); + } + blobmsg_close_array(&b, c); + + ubus_send_reply(ctx, req, b.head); + + return 0; +} + +enum { + NR_SET_LIST, + __NR_SET_LIST_MAX +}; + +static const struct blobmsg_policy nr_set_policy[__NR_SET_LIST_MAX] = { + [NR_SET_LIST] = { "list", BLOBMSG_TYPE_ARRAY }, +}; + + +static void +hostapd_rrm_nr_clear(struct hostapd_data *hapd) +{ + struct hostapd_neighbor_entry *nr; + +restart: + dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) { + if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN)) + continue; + + hostapd_neighbor_remove(hapd, nr->bssid, &nr->ssid); + goto restart; + } +} + +static int +hostapd_rrm_nr_set(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + static const struct blobmsg_policy nr_e_policy[] = { + { .type = BLOBMSG_TYPE_STRING }, + { .type = BLOBMSG_TYPE_STRING }, + { .type = BLOBMSG_TYPE_STRING }, + }; + struct hostapd_data *hapd = get_hapd_from_object(obj); + struct blob_attr *tb_l[__NR_SET_LIST_MAX]; + struct blob_attr *tb[ARRAY_SIZE(nr_e_policy)]; + struct blob_attr *cur; + int ret = 0; + int rem; + + if (!(hapd->conf->radio_measurements[0] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) + return UBUS_STATUS_NOT_SUPPORTED; + + blobmsg_parse(nr_set_policy, __NR_SET_LIST_MAX, tb_l, blob_data(msg), blob_len(msg)); + if (!tb_l[NR_SET_LIST]) + return UBUS_STATUS_INVALID_ARGUMENT; + + hostapd_rrm_nr_clear(hapd); + blobmsg_for_each_attr(cur, tb_l[NR_SET_LIST], rem) { + struct wpa_ssid_value ssid; + struct wpabuf *data; + u8 bssid[ETH_ALEN]; + char *s; + + blobmsg_parse_array(nr_e_policy, ARRAY_SIZE(nr_e_policy), tb, blobmsg_data(cur), blobmsg_data_len(cur)); + if (!tb[0] || !tb[1] || !tb[2]) + goto invalid; + + s = blobmsg_get_string(tb[0]); + if (hwaddr_aton(s, bssid)) + goto invalid; + + s = blobmsg_get_string(tb[1]); + ssid.ssid_len = strlen(s); + if (ssid.ssid_len > sizeof(ssid.ssid)) + goto invalid; + + memcpy(&ssid, s, ssid.ssid_len); + data = wpabuf_parse_bin(blobmsg_get_string(tb[2])); + if (!data) + goto invalid; + + hostapd_neighbor_set(hapd, bssid, &ssid, data, NULL, NULL, 0); + wpabuf_free(data); + continue; + +invalid: + ret = UBUS_STATUS_INVALID_ARGUMENT; + } + + return 0; +} + static const struct ubus_method bss_methods[] = { UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients), UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy), @@ -459,6 +625,9 @@ static const struct ubus_method bss_methods[] = { #endif UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy), UBUS_METHOD("notify_response", hostapd_notify_response, notify_policy), + UBUS_METHOD_NOARG("rrm_nr_get_own", hostapd_rrm_nr_get_own), + UBUS_METHOD_NOARG("rrm_nr_list", hostapd_rrm_nr_list), + UBUS_METHOD("rrm_nr_set", hostapd_rrm_nr_set, nr_set_policy), }; static struct ubus_object_type bss_object_type =