MBO: Add support to ignore association disallowed set by AP
[mech_eap.git] / hostapd / ctrl_iface.c
index 237f02c..d7db4a7 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "utils/module_tests.h"
 #include "common/version.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ctrl_iface_common.h"
@@ -47,6 +48,8 @@
 #include "ap/wnm_ap.h"
 #include "ap/wpa_auth.h"
 #include "ap/beacon.h"
+#include "ap/neighbor_db.h"
+#include "ap/rrm.h"
 #include "wps/wps_defs.h"
 #include "wps/wps.h"
 #include "fst/fst_ctrl_iface.h"
@@ -1588,8 +1591,8 @@ static u16 ipv4_hdr_checksum(const void *buf, size_t len)
 #define HWSIM_PACKETLEN 1500
 #define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
 
-void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
-                         size_t len)
+static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
+                                size_t len)
 {
        struct hostapd_data *hapd = ctx;
        const struct ether_header *eth;
@@ -1776,8 +1779,6 @@ done:
 static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd)
 {
 #ifdef WPA_TRACE_BFD
-       extern char wpa_trace_fail_func[256];
-       extern unsigned int wpa_trace_fail_after;
        char *pos;
 
        wpa_trace_fail_after = atoi(cmd);
@@ -1801,9 +1802,6 @@ static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd,
                                       char *buf, size_t buflen)
 {
 #ifdef WPA_TRACE_BFD
-       extern char wpa_trace_fail_func[256];
-       extern unsigned int wpa_trace_fail_after;
-
        return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
                           wpa_trace_fail_func);
 #else /* WPA_TRACE_BFD */
@@ -1815,8 +1813,6 @@ static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd,
 static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd)
 {
 #ifdef WPA_TRACE_BFD
-       extern char wpa_trace_test_fail_func[256];
-       extern unsigned int wpa_trace_test_fail_after;
        char *pos;
 
        wpa_trace_test_fail_after = atoi(cmd);
@@ -1840,9 +1836,6 @@ static int hostapd_ctrl_get_fail(struct hostapd_data *hapd,
                                 char *buf, size_t buflen)
 {
 #ifdef WPA_TRACE_BFD
-       extern char wpa_trace_test_fail_func[256];
-       extern unsigned int wpa_trace_test_fail_after;
-
        return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
                           wpa_trace_test_fail_func);
 #else /* WPA_TRACE_BFD */
@@ -2047,6 +2040,9 @@ static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
        struct hostapd_sta_info *info;
        struct os_reltime now;
 
+       if (!iface->num_sta_seen)
+               return 0;
+
        sta_track_expire(iface, 0);
 
        pos = buf;
@@ -2071,6 +2067,224 @@ static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
 #endif /* NEED_AP_MLME */
 
 
+static int hostapd_ctrl_iface_req_lci(struct hostapd_data *hapd,
+                                     const char *cmd)
+{
+       u8 addr[ETH_ALEN];
+
+       if (hwaddr_aton(cmd, addr)) {
+               wpa_printf(MSG_INFO, "CTRL: REQ_LCI: Invalid MAC address");
+               return -1;
+       }
+
+       return hostapd_send_lci_req(hapd, addr);
+}
+
+
+static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd)
+{
+       u8 addr[ETH_ALEN];
+       char *token, *context = NULL;
+       int random_interval, min_ap;
+       u8 responders[ETH_ALEN * RRM_RANGE_REQ_MAX_RESPONDERS];
+       unsigned int n_responders;
+
+       token = str_token(cmd, " ", &context);
+       if (!token || hwaddr_aton(token, addr)) {
+               wpa_printf(MSG_INFO,
+                          "CTRL: REQ_RANGE - Bad destination address");
+               return -1;
+       }
+
+       token = str_token(cmd, " ", &context);
+       if (!token)
+               return -1;
+
+       random_interval = atoi(token);
+       if (random_interval < 0 || random_interval > 0xffff)
+               return -1;
+
+       token = str_token(cmd, " ", &context);
+       if (!token)
+               return -1;
+
+       min_ap = atoi(token);
+       if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP)
+               return -1;
+
+       n_responders = 0;
+       while ((token = str_token(cmd, " ", &context))) {
+               if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) {
+                       wpa_printf(MSG_INFO,
+                                  "CTRL: REQ_RANGE: Too many responders");
+                       return -1;
+               }
+
+               if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) {
+                       wpa_printf(MSG_INFO,
+                                  "CTRL: REQ_RANGE: Bad responder address");
+                       return -1;
+               }
+
+               n_responders++;
+       }
+
+       if (!n_responders) {
+               wpa_printf(MSG_INFO,
+                          "CTRL: REQ_RANGE - No FTM responder address");
+               return -1;
+       }
+
+       return hostapd_send_range_req(hapd, addr, random_interval, min_ap,
+                                     responders, n_responders);
+}
+
+
+static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf)
+{
+       struct wpa_ssid_value ssid;
+       u8 bssid[ETH_ALEN];
+       struct wpabuf *nr, *lci = NULL, *civic = NULL;
+       char *tmp;
+       int ret;
+
+       if (!(hapd->conf->radio_measurements[0] &
+             WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
+               wpa_printf(MSG_ERROR,
+                          "CTRL: SET_NEIGHBOR: Neighbor report is not enabled");
+               return -1;
+       }
+
+       if (hwaddr_aton(buf, bssid)) {
+               wpa_printf(MSG_ERROR, "CTRL: SET_NEIGHBOR: Bad BSSID");
+               return -1;
+       }
+
+       tmp = os_strstr(buf, "ssid=");
+       if (!tmp || ssid_parse(tmp + 5, &ssid)) {
+               wpa_printf(MSG_ERROR,
+                          "CTRL: SET_NEIGHBOR: Bad or missing SSID");
+               return -1;
+       }
+       buf = os_strchr(tmp + 6, tmp[5] == '"' ? '"' : ' ');
+       if (!buf)
+               return -1;
+
+       tmp = os_strstr(buf, "nr=");
+       if (!tmp) {
+               wpa_printf(MSG_ERROR,
+                          "CTRL: SET_NEIGHBOR: Missing Neighbor Report element");
+               return -1;
+       }
+
+       buf = os_strchr(tmp, ' ');
+       if (buf)
+               *buf++ = '\0';
+
+       nr = wpabuf_parse_bin(tmp + 3);
+       if (!nr) {
+               wpa_printf(MSG_ERROR,
+                          "CTRL: SET_NEIGHBOR: Bad Neighbor Report element");
+               return -1;
+       }
+
+       if (!buf)
+               goto set;
+
+       tmp = os_strstr(buf, "lci=");
+       if (tmp) {
+               buf = os_strchr(tmp, ' ');
+               if (buf)
+                       *buf++ = '\0';
+               lci = wpabuf_parse_bin(tmp + 4);
+               if (!lci) {
+                       wpa_printf(MSG_ERROR,
+                                  "CTRL: SET_NEIGHBOR: Bad LCI subelement");
+                       wpabuf_free(nr);
+                       return -1;
+               }
+       }
+
+       if (!buf)
+               goto set;
+
+       tmp = os_strstr(buf, "civic=");
+       if (tmp) {
+               buf = os_strchr(tmp, ' ');
+               if (buf)
+                       *buf++ = '\0';
+               civic = wpabuf_parse_bin(tmp + 6);
+               if (!civic) {
+                       wpa_printf(MSG_ERROR,
+                                  "CTRL: SET_NEIGHBOR: Bad civic subelement");
+                       wpabuf_free(nr);
+                       wpabuf_free(lci);
+                       return -1;
+               }
+       }
+
+set:
+       ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic);
+
+       wpabuf_free(nr);
+       wpabuf_free(lci);
+       wpabuf_free(civic);
+
+       return ret;
+}
+
+
+static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd,
+                                             char *buf)
+{
+       struct wpa_ssid_value ssid;
+       u8 bssid[ETH_ALEN];
+       char *tmp;
+
+       if (hwaddr_aton(buf, bssid)) {
+               wpa_printf(MSG_ERROR, "CTRL: REMOVE_NEIGHBOR: Bad BSSID");
+               return -1;
+       }
+
+       tmp = os_strstr(buf, "ssid=");
+       if (!tmp || ssid_parse(tmp + 5, &ssid)) {
+               wpa_printf(MSG_ERROR,
+                          "CTRL: REMOVE_NEIGHBORr: Bad or missing SSID");
+               return -1;
+       }
+
+       return hostapd_neighbor_remove(hapd, bssid, &ssid);
+}
+
+
+static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf,
+                                    size_t buflen)
+{
+       int ret, i;
+       char *pos, *end;
+
+       ret = os_snprintf(buf, buflen, "%016llX:\n",
+                         (long long unsigned) iface->drv_flags);
+       if (os_snprintf_error(buflen, ret))
+               return -1;
+
+       pos = buf + ret;
+       end = buf + buflen;
+
+       for (i = 0; i < 64; i++) {
+               if (iface->drv_flags & (1LLU << i)) {
+                       ret = os_snprintf(pos, end - pos, "%s\n",
+                                         driver_flag_to_string(1LLU << i));
+                       if (os_snprintf_error(end - pos, ret))
+                               return -1;
+                       pos += ret;
+               }
+       }
+
+       return pos - buf;
+}
+
+
 static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
                                              char *buf, char *reply,
                                              int reply_size,
@@ -2153,6 +2367,14 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
        } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
                if (hostapd_ctrl_iface_disassociate(hapd, buf + 13))
                        reply_len = -1;
+#ifdef CONFIG_TAXONOMY
+       } else if (os_strncmp(buf, "SIGNATURE ", 10) == 0) {
+               reply_len = hostapd_ctrl_iface_signature(hapd, buf + 10,
+                                                        reply, reply_size);
+#endif /* CONFIG_TAXONOMY */
+       } else if (os_strncmp(buf, "POLL_STA ", 9) == 0) {
+               if (hostapd_ctrl_iface_poll_sta(hapd, buf + 9))
+                       reply_len = -1;
        } else if (os_strcmp(buf, "STOP_AP") == 0) {
                if (hostapd_ctrl_iface_stop_ap(hapd))
                        reply_len = -1;
@@ -2307,6 +2529,26 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
                reply_len = hostapd_ctrl_iface_track_sta_list(
                        hapd, reply, reply_size);
 #endif /* NEED_AP_MLME */
+       } else if (os_strcmp(buf, "PMKSA") == 0) {
+               reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply,
+                                                         reply_size);
+       } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
+               hostapd_ctrl_iface_pmksa_flush(hapd);
+       } else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) {
+               if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) {
+               if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "REQ_LCI ", 8) == 0) {
+               if (hostapd_ctrl_iface_req_lci(hapd, buf + 8))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) {
+               if (hostapd_ctrl_iface_req_range(hapd, buf + 10))
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
+               reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply,
+                                                     reply_size);
        } else {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
                reply_len = 16;
@@ -2444,6 +2686,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
 #ifdef CONFIG_CTRL_IFACE_UDP
        int port = HOSTAPD_CTRL_IFACE_PORT;
        char p[32] = { 0 };
+       char port_str[40], *tmp;
+       char *pos;
        struct addrinfo hints = { 0 }, *res, *saveres;
        int n;
 
@@ -2455,6 +2699,16 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
        if (hapd->conf->ctrl_interface == NULL)
                return 0;
 
+       pos = os_strstr(hapd->conf->ctrl_interface, "udp:");
+       if (pos) {
+               pos += 4;
+               port = atoi(pos);
+               if (port <= 0) {
+                       wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port");
+                       goto fail;
+               }
+       }
+
        dl_list_init(&hapd->ctrl_dst);
        hapd->ctrl_sock = -1;
        os_get_random(cookie, COOKIE_LEN);
@@ -2489,7 +2743,7 @@ try_again:
        if (bind(hapd->ctrl_sock, res->ai_addr, res->ai_addrlen) < 0) {
                port--;
                if ((HOSTAPD_CTRL_IFACE_PORT - port) <
-                   HOSTAPD_CTRL_IFACE_PORT_LIMIT)
+                   HOSTAPD_CTRL_IFACE_PORT_LIMIT && !pos)
                        goto try_again;
                wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
                goto fail;
@@ -2497,6 +2751,12 @@ try_again:
 
        freeaddrinfo(saveres);
 
+       os_snprintf(port_str, sizeof(port_str), "udp:%d", port);
+       tmp = os_strdup(port_str);
+       if (tmp) {
+               os_free(hapd->conf->ctrl_interface);
+               hapd->conf->ctrl_interface = tmp;
+       }
        wpa_printf(MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
 
        if (eloop_register_read_sock(hapd->ctrl_sock,
@@ -2903,6 +3163,51 @@ error_return:
 
 
 static int
+hostapd_global_ctrl_iface_interfaces(struct hapd_interfaces *interfaces,
+                                    const char *input,
+                                    char *reply, int reply_size)
+{
+       size_t i, j;
+       int res;
+       char *pos, *end;
+       struct hostapd_iface *iface;
+       int show_ctrl = 0;
+
+       if (input)
+               show_ctrl = !!os_strstr(input, "ctrl");
+
+       pos = reply;
+       end = reply + reply_size;
+
+       for (i = 0; i < interfaces->count; i++) {
+               iface = interfaces->iface[i];
+
+               for (j = 0; j < iface->num_bss; j++) {
+                       struct hostapd_bss_config *conf;
+
+                       conf = iface->conf->bss[j];
+                       if (show_ctrl)
+                               res = os_snprintf(pos, end - pos,
+                                                 "%s ctrl_iface=%s\n",
+                                                 conf->iface,
+                                                 conf->ctrl_interface ?
+                                                 conf->ctrl_interface : "N/A");
+                       else
+                               res = os_snprintf(pos, end - pos, "%s\n",
+                                                 conf->iface);
+                       if (os_snprintf_error(end - pos, res)) {
+                               *pos = '\0';
+                               return pos - reply;
+                       }
+                       pos += res;
+               }
+       }
+
+       return pos - reply;
+}
+
+
+static int
 hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces,
                                      char *cmd)
 {
@@ -3074,7 +3379,6 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
                        reply_len = -1;
 #ifdef CONFIG_MODULE_TESTS
        } else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
-               int hapd_module_tests(void);
                if (hapd_module_tests() < 0)
                        reply_len = -1;
 #endif /* CONFIG_MODULE_TESTS */
@@ -3098,6 +3402,11 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
                        reply_len = os_snprintf(reply, reply_size, "OK\n");
                else
                        reply_len = -1;
+       } else if (os_strncmp(buf, "INTERFACES", 10) == 0) {
+               reply_len = hostapd_global_ctrl_iface_interfaces(
+                       interfaces, buf + 10, reply, sizeof(buffer));
+       } else if (os_strcmp(buf, "TERMINATE") == 0) {
+               eloop_terminate();
        } else {
                wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
                           "ignored");
@@ -3147,6 +3456,7 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
 #ifdef CONFIG_CTRL_IFACE_UDP
        int port = HOSTAPD_GLOBAL_CTRL_IFACE_PORT;
        char p[32] = { 0 };
+       char *pos;
        struct addrinfo hints = { 0 }, *res, *saveres;
        int n;
 
@@ -3158,6 +3468,16 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
        if (interface->global_iface_path == NULL)
                return 0;
 
+       pos = os_strstr(interface->global_iface_path, "udp:");
+       if (pos) {
+               pos += 4;
+               port = atoi(pos);
+               if (port <= 0) {
+                       wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port");
+                       goto fail;
+               }
+       }
+
        dl_list_init(&interface->global_ctrl_dst);
        interface->global_ctrl_sock = -1;
        os_get_random(gcookie, COOKIE_LEN);
@@ -3193,7 +3513,7 @@ try_again:
            0) {
                port++;
                if ((port - HOSTAPD_GLOBAL_CTRL_IFACE_PORT) <
-                   HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT)
+                   HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos)
                        goto try_again;
                wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
                goto fail;