Add wpa_supplicant EVENT_TEST control interface command
[mech_eap.git] / wpa_supplicant / ctrl_iface.c
index eab0750..02d0cc2 100644 (file)
@@ -28,6 +28,7 @@
 #include "rsn_supp/pmksa_cache.h"
 #include "l2_packet/l2_packet.h"
 #include "wps/wps.h"
+#include "fst/fst.h"
 #include "fst/fst_ctrl_iface.h"
 #include "config.h"
 #include "wpa_supplicant_i.h"
@@ -285,6 +286,30 @@ static int wpas_ctrl_pno(struct wpa_supplicant *wpa_s, char *cmd)
 }
 
 
+static int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *band)
+{
+       union wpa_event_data event;
+
+       if (os_strcmp(band, "AUTO") == 0)
+               wpa_s->setband = WPA_SETBAND_AUTO;
+       else if (os_strcmp(band, "5G") == 0)
+               wpa_s->setband = WPA_SETBAND_5G;
+       else if (os_strcmp(band, "2G") == 0)
+               wpa_s->setband = WPA_SETBAND_2G;
+       else
+               return -1;
+
+       if (wpa_drv_setband(wpa_s, wpa_s->setband) == 0) {
+               os_memset(&event, 0, sizeof(event));
+               event.channel_list_changed.initiator = REGDOM_SET_BY_USER;
+               event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN;
+               wpa_supplicant_event(wpa_s, EVENT_CHANNEL_LIST_CHANGED, &event);
+       }
+
+       return 0;
+}
+
+
 static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
                                         char *cmd)
 {
@@ -448,14 +473,7 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
                ret = wpas_ctrl_set_blob(wpa_s, value);
 #endif /* CONFIG_NO_CONFIG_BLOBS */
        } else if (os_strcasecmp(cmd, "setband") == 0) {
-               if (os_strcmp(value, "AUTO") == 0)
-                       wpa_s->setband = WPA_SETBAND_AUTO;
-               else if (os_strcmp(value, "5G") == 0)
-                       wpa_s->setband = WPA_SETBAND_5G;
-               else if (os_strcmp(value, "2G") == 0)
-                       wpa_s->setband = WPA_SETBAND_2G;
-               else
-                       ret = -1;
+               ret = wpas_ctrl_set_band(wpa_s, value);
        } else {
                value[-1] = '=';
                ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -2118,45 +2136,6 @@ static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s,
 }
 
 
-static const char * debug_level_str(int level)
-{
-       switch (level) {
-       case MSG_EXCESSIVE:
-               return "EXCESSIVE";
-       case MSG_MSGDUMP:
-               return "MSGDUMP";
-       case MSG_DEBUG:
-               return "DEBUG";
-       case MSG_INFO:
-               return "INFO";
-       case MSG_WARNING:
-               return "WARNING";
-       case MSG_ERROR:
-               return "ERROR";
-       default:
-               return "?";
-       }
-}
-
-
-static int str_to_debug_level(const char *s)
-{
-       if (os_strcasecmp(s, "EXCESSIVE") == 0)
-               return MSG_EXCESSIVE;
-       if (os_strcasecmp(s, "MSGDUMP") == 0)
-               return MSG_MSGDUMP;
-       if (os_strcasecmp(s, "DEBUG") == 0)
-               return MSG_DEBUG;
-       if (os_strcasecmp(s, "INFO") == 0)
-               return MSG_INFO;
-       if (os_strcasecmp(s, "WARNING") == 0)
-               return MSG_WARNING;
-       if (os_strcasecmp(s, "ERROR") == 0)
-               return MSG_ERROR;
-       return -1;
-}
-
-
 static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
                                               char *cmd, char *buf,
                                               size_t buflen)
@@ -2189,7 +2168,7 @@ static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
                }
        }
 
-       if (cmd && os_strlen(cmd)) {
+       if (os_strlen(cmd)) {
                int level = str_to_debug_level(cmd);
                if (level < 0)
                        return -1;
@@ -2765,6 +2744,8 @@ static int wpa_supplicant_ctrl_iface_select_network(
                }
        }
 
+       wpa_s->scan_min_time.sec = 0;
+       wpa_s->scan_min_time.usec = 0;
        wpa_supplicant_select_network(wpa_s, ssid);
 
        return 0;
@@ -2802,6 +2783,8 @@ static int wpa_supplicant_ctrl_iface_enable_network(
                        return 0;
                }
        }
+       wpa_s->scan_min_time.sec = 0;
+       wpa_s->scan_min_time.usec = 0;
        wpa_supplicant_enable_network(wpa_s, ssid);
 
        return 0;
@@ -3056,19 +3039,19 @@ static int wpa_supplicant_ctrl_iface_get_network(
        *name++ = '\0';
 
        id = atoi(cmd);
-       wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
+       wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
                   id, name);
 
        ssid = wpa_config_get_network(wpa_s->conf, id);
        if (ssid == NULL) {
-               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
+               wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Could not find network "
                           "id=%d", id);
                return -1;
        }
 
        value = wpa_config_get_no_key(ssid, name);
        if (value == NULL) {
-               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network "
+               wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Failed to get network "
                           "variable '%s'", name);
                return -1;
        }
@@ -3943,6 +3926,15 @@ static int wpa_supplicant_ctrl_iface_get_capability(
        }
 #endif /* CONFIG_EPR */
 
+#ifdef CONFIG_FIPS
+       if (os_strcmp(field, "fips") == 0) {
+               res = os_snprintf(buf, buflen, "FIPS");
+               if (os_snprintf_error(buflen, res))
+                       return -1;
+               return res;
+       }
+#endif /* CONFIG_FIPS */
+
        wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
                   field);
 
@@ -4192,9 +4184,10 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
        if (mask & WPA_BSS_MASK_WPS_SCAN) {
                ie = (const u8 *) (bss + 1);
                ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end);
-               if (ret < 0 || ret >= end - pos)
+               if (ret >= end - pos)
                        return 0;
-               pos += ret;
+               if (ret > 0)
+                       pos += ret;
        }
 #endif /* CONFIG_WPS */
 
@@ -4651,6 +4644,48 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
 }
 
 
+static int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt)
+{
+       const char *last = NULL;
+       const char *token;
+       long int token_len;
+       unsigned int i;
+
+       /* Expected predefined CPT names delimited by ':' */
+       for (i = 0; (token = cstr_token(pos, ": \t", &last)); i++) {
+               if (i >= P2PS_FEATURE_CAPAB_CPT_MAX) {
+                       wpa_printf(MSG_ERROR,
+                                  "P2PS: CPT name list is too long, expected up to %d names",
+                                  P2PS_FEATURE_CAPAB_CPT_MAX);
+                       cpt[0] = 0;
+                       return -1;
+               }
+
+               token_len = last - token;
+
+               if (token_len  == 3 &&
+                   os_memcmp(token, "UDP", token_len) == 0) {
+                       cpt[i] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+               } else if (token_len == 3 &&
+                          os_memcmp(token, "MAC", token_len) == 0) {
+                       cpt[i] = P2PS_FEATURE_CAPAB_MAC_TRANSPORT;
+               } else {
+                       wpa_printf(MSG_ERROR,
+                                  "P2PS: Unsupported CPT name '%s'", token);
+                       cpt[0] = 0;
+                       return -1;
+               }
+
+               if (isblank(*last)) {
+                       i++;
+                       break;
+               }
+       }
+       cpt[i] = 0;
+       return 0;
+}
+
+
 static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd)
 {
        struct p2ps_provision *p2ps_prov;
@@ -4659,6 +4694,7 @@ static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd)
        char *info = NULL;
        u8 role = P2PS_SETUP_NONE;
        long long unsigned val;
+       int i;
 
        pos = os_strstr(cmd, "info=");
        if (pos) {
@@ -4717,6 +4753,18 @@ static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd)
        if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac))
                goto invalid_args;
 
+       pos = os_strstr(cmd, "cpt=");
+       if (pos) {
+               if (p2ps_ctrl_parse_cpt_priority(pos + 4,
+                                                p2ps_prov->cpt_priority))
+                       goto invalid_args;
+       } else {
+               p2ps_prov->cpt_priority[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+       }
+
+       for (i = 0; p2ps_prov->cpt_priority[i]; i++)
+               p2ps_prov->cpt_mask |= p2ps_prov->cpt_priority[i];
+
        /* force conncap with tstCap (no sanity checks) */
        pos = os_strstr(cmd, "tstCap=");
        if (pos) {
@@ -4793,6 +4841,8 @@ static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd)
        if (!p2ps_prov)
                return -1;
 
+       p2ps_prov->pd_seeker = 1;
+
        return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
                                  p2ps_prov);
 }
@@ -5212,6 +5262,8 @@ static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s,
        char *adv_str;
        u32 auto_accept, adv_id, svc_state, config_methods;
        char *svc_info = NULL;
+       char *cpt_prio_str;
+       u8 cpt_prio[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
 
        pos = os_strchr(cmd, ' ');
        if (pos == NULL)
@@ -5284,6 +5336,19 @@ static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s,
        if (pos != NULL)
                *pos++ = '\0';
 
+       cpt_prio_str = (pos && pos[0]) ? os_strstr(pos, "cpt=") : NULL;
+       if (cpt_prio_str) {
+               pos = os_strchr(pos, ' ');
+               if (pos != NULL)
+                       *pos++ = '\0';
+
+               if (p2ps_ctrl_parse_cpt_priority(cpt_prio_str + 4, cpt_prio))
+                       return -1;
+       } else {
+               cpt_prio[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+               cpt_prio[1] = 0;
+       }
+
        /* Service and Response Information are optional */
        if (pos && pos[0]) {
                size_t len;
@@ -5301,7 +5366,7 @@ static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s,
 
        return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str,
                                        (u8) svc_state, (u16) config_methods,
-                                       svc_info);
+                                       svc_info, cpt_prio);
 }
 
 
@@ -5539,7 +5604,7 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
        }
 
        return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht,
-                                            NULL, 0);
+                                            NULL, 0, 0);
 }
 
 
@@ -5703,7 +5768,7 @@ static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s,
                           freq->min, freq->max);
        }
 
-       wpas_p2p_update_channel_list(wpa_s);
+       wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW);
        return 0;
 }
 
@@ -5920,6 +5985,7 @@ static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s)
        os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
        wpa_s->force_long_sd = 0;
        wpas_p2p_stop_find(wpa_s);
+       wpa_s->parent->p2ps_method_config_any = 0;
        if (wpa_s->global->p2p)
                p2p_flush(wpa_s->global->p2p);
 }
@@ -6566,6 +6632,53 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
 }
 
 
+static int wpas_ctrl_iface_get_pref_freq_list(
+       struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+       unsigned int freq_list[100], num = 100, i;
+       int ret;
+       enum wpa_driver_if_type iface_type;
+       char *pos, *end;
+
+       pos = buf;
+       end = buf + buflen;
+
+       /* buf: "<interface_type>" */
+       if (os_strcmp(cmd, "STATION") == 0)
+               iface_type = WPA_IF_STATION;
+       else if (os_strcmp(cmd, "AP") == 0)
+               iface_type = WPA_IF_AP_BSS;
+       else if (os_strcmp(cmd, "P2P_GO") == 0)
+               iface_type = WPA_IF_P2P_GO;
+       else if (os_strcmp(cmd, "P2P_CLIENT") == 0)
+               iface_type = WPA_IF_P2P_CLIENT;
+       else if (os_strcmp(cmd, "IBSS") == 0)
+               iface_type = WPA_IF_IBSS;
+       else if (os_strcmp(cmd, "TDLS") == 0)
+               iface_type = WPA_IF_TDLS;
+       else
+               return -1;
+
+       wpa_printf(MSG_DEBUG,
+                  "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)",
+                  iface_type, buf);
+
+       ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list);
+       if (ret)
+               return -1;
+
+       for (i = 0; i < num; i++) {
+               ret = os_snprintf(pos, end - pos, "%s%u",
+                                 i > 0 ? "," : "", freq_list[i]);
+               if (os_snprintf_error(end - pos, ret))
+                       return -1;
+               pos += ret;
+       }
+
+       return pos - buf;
+}
+
+
 static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
                                      size_t buflen)
 {
@@ -6688,6 +6801,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
        p2p_wpa_s->p2p_disable_ip_addr_req = 0;
        os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range);
        p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL;
+       p2p_wpa_s->global->p2p_go_avoid_freq.num = 0;
        p2p_wpa_s->global->pending_p2ps_group = 0;
 #endif /* CONFIG_P2P */
 
@@ -7621,6 +7735,33 @@ static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s,
 #endif /* WPA_TRACE_BFD */
 }
 
+
+static void wpas_ctrl_event_test_cb(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       int i, count = (intptr_t) timeout_ctx;
+
+       wpa_printf(MSG_DEBUG, "TEST: Send %d control interface event messages",
+                  count);
+       for (i = 0; i < count; i++) {
+               wpa_msg_ctrl(wpa_s, MSG_INFO, "TEST-EVENT-MESSAGE %d/%d",
+                            i + 1, count);
+       }
+}
+
+
+static int wpas_ctrl_event_test(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+       int count;
+
+       count = atoi(cmd);
+       if (count <= 0)
+               return -1;
+
+       return eloop_register_timeout(0, 0, wpas_ctrl_event_test_cb, wpa_s,
+                                     (void *) (intptr_t) count);
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
@@ -7998,6 +8139,19 @@ static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s,
 }
 
 
+static int wpas_ctrl_cmd_debug_level(const char *cmd)
+{
+       if (os_strcmp(cmd, "PING") == 0 ||
+           os_strncmp(cmd, "BSS ", 4) == 0 ||
+           os_strncmp(cmd, "GET_NETWORK ", 12) == 0 ||
+           os_strncmp(cmd, "STATUS", 6) == 0 ||
+           os_strncmp(cmd, "STA ", 4) == 0 ||
+           os_strncmp(cmd, "STA-", 4) == 0)
+               return MSG_EXCESSIVE;
+       return MSG_DEBUG;
+}
+
+
 char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                                         char *buf, size_t *resp_len)
 {
@@ -8021,9 +8175,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
                                      (const u8 *) buf, os_strlen(buf));
        } else {
-               int level = MSG_DEBUG;
-               if (os_strcmp(buf, "PING") == 0)
-                       level = MSG_EXCESSIVE;
+               int level = wpas_ctrl_cmd_debug_level(buf);
                wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
        }
 
@@ -8644,6 +8796,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                        reply_len = -1;
        } else if (os_strcmp(buf, "GET_FAIL") == 0) {
                reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size);
+       } else if (os_strncmp(buf, "EVENT_TEST ", 11) == 0) {
+               if (wpas_ctrl_event_test(wpa_s, buf + 11) < 0)
+                       reply_len = -1;
 #endif /* CONFIG_TESTING_OPTIONS */
        } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
                if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
@@ -8662,6 +8817,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) {
                if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14))
                        reply_len = -1;
+       } else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) {
+               reply_len = wpas_ctrl_iface_get_pref_freq_list(
+                       wpa_s, buf + 19, reply, reply_size);
        } else {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
                reply_len = 16;
@@ -9193,6 +9351,10 @@ static int wpas_global_ctrl_iface_fst_attach(struct wpa_global *global,
        if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
                wpa_s = wpa_supplicant_get_iface(global, ifname);
                if (wpa_s) {
+                       if (wpa_s->fst) {
+                               wpa_printf(MSG_INFO, "FST: Already attached");
+                               return -1;
+                       }
                        fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
                        wpa_s->fst = fst_attach(ifname, wpa_s->own_addr,
                                                &iface_obj, &cfg);