hostapd: add support for turning on 802.11k/v features via ubus
[openwrt/staging/mkresin.git] / package / network / services / hostapd / src / src / ap / ubus.c
index 715c45604930e2b7e86f4218629501a77414b48d..59b92ab1c9f1f4b46c7888a52f925f331aed259c 100644 (file)
@@ -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,270 @@ 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);
+}
+
+enum {
+       BSS_MGMT_EN_NEIGHBOR,
+       BSS_MGMT_EN_BEACON,
+#ifdef CONFIG_WNM_AP
+       BSS_MGMT_EN_BSS_TRANSITION,
+#endif
+       __BSS_MGMT_EN_MAX
+};
+
+static bool
+__hostapd_bss_mgmt_enable_f(struct hostapd_data *hapd, int flag)
+{
+       struct hostapd_bss_config *bss = hapd->conf;
+       uint32_t flags;
+
+       switch (flag) {
+       case BSS_MGMT_EN_NEIGHBOR:
+               if (bss->radio_measurements[0] &
+                   WLAN_RRM_CAPS_NEIGHBOR_REPORT)
+                       return false;
+
+               bss->radio_measurements[0] |=
+                       WLAN_RRM_CAPS_NEIGHBOR_REPORT;
+               hostapd_set_own_neighbor_report(hapd);
+               return true;
+       case BSS_MGMT_EN_BEACON:
+               flags = WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
+                       WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
+                       WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
+
+               if (bss->radio_measurements[0] & flags == flags)
+                       return false;
+
+               bss->radio_measurements[0] |= (u8) flags;
+               return true;
+#ifdef CONFIG_WNM_AP
+       case BSS_MGMT_EN_BSS_TRANSITION:
+               if (bss->bss_transition)
+                       return false;
+
+               bss->bss_transition = 1;
+               return true;
+#endif
+       }
+}
+
+static void
+__hostapd_bss_mgmt_enable(struct hostapd_data *hapd, uint32_t flags)
+{
+       bool update = false;
+       int i;
+
+       for (i = 0; i < __BSS_MGMT_EN_MAX; i++) {
+               if (!(flags & (1 << i)))
+                       continue;
+
+               update |= __hostapd_bss_mgmt_enable_f(hapd, i);
+       }
+
+       if (update)
+               ieee802_11_update_beacons(hapd->iface);
+}
+
+
+static const struct blobmsg_policy bss_mgmt_enable_policy[__BSS_MGMT_EN_MAX] = {
+       [BSS_MGMT_EN_NEIGHBOR] = { "neighbor_report", BLOBMSG_TYPE_BOOL },
+       [BSS_MGMT_EN_BEACON] = { "beacon_report", BLOBMSG_TYPE_BOOL },
+#ifdef CONFIG_WNM_AP
+       [BSS_MGMT_EN_BSS_TRANSITION] = { "bss_transition", BLOBMSG_TYPE_BOOL },
+#endif
+};
+
+static int
+hostapd_bss_mgmt_enable(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 blob_attr *tb[__BSS_MGMT_EN_MAX];
+       struct blob_attr *cur;
+       uint32_t flags = 0;
+       int i;
+       bool neigh = false, beacon = false;
+
+       blobmsg_parse(bss_mgmt_enable_policy, __BSS_MGMT_EN_MAX, tb, blob_data(msg), blob_len(msg));
+
+       for (i = 0; i < ARRAY_SIZE(tb); i++) {
+               if (!tb[i] || !blobmsg_get_bool(tb[i]))
+                       continue;
+
+               flags |= (1 << i);
+       }
+
+       __hostapd_bss_mgmt_enable(hapd, flags);
+}
+
+
+static void
+hostapd_rrm_nr_enable(struct hostapd_data *hapd)
+{
+       __hostapd_bss_mgmt_enable(hapd, 1 << BSS_MGMT_EN_NEIGHBOR);
+}
+
+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;
+
+       hostapd_rrm_nr_enable(hapd);
+
+       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;
+
+       hostapd_rrm_nr_enable(hapd);
+       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;
+
+       hostapd_rrm_nr_enable(hapd);
+
+       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 +725,10 @@ 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("bss_mgmt_enable", hostapd_bss_mgmt_enable, bss_mgmt_enable_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 =
@@ -574,18 +844,14 @@ int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_req
 
 void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *addr)
 {
-       char mac[18];
-
        if (!hapd->ubus.obj.has_subscribers)
                return;
 
        if (!addr)
                return;
 
-       snprintf(mac, sizeof(mac), MACSTR, MAC2STR(addr));
-
        blob_buf_init(&b, 0);
-       blobmsg_add_macaddr(&b, "address", mac);
+       blobmsg_add_macaddr(&b, "address", addr);
 
        ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
 }