QCA vendor command support to set band to driver
[mech_eap.git] / wpa_supplicant / ctrl_iface.c
index 377b9ed..41f9090 100644 (file)
@@ -28,6 +28,8 @@
 #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"
 #include "driver_i.h"
@@ -153,7 +155,8 @@ static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val)
                        }
                        ssid = ns;
 
-                       if ((end - pos) & 0x01 || end - pos > 2 * 32 ||
+                       if ((end - pos) & 0x01 ||
+                           end - pos > 2 * SSID_MAX_LEN ||
                            hexstr2bin(pos, ssid[ssid_count].ssid,
                                       (end - pos) / 2) < 0) {
                                os_free(ssid);
@@ -283,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)
 {
@@ -446,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);
@@ -759,6 +779,33 @@ static int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(
        return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer);
 }
 
+
+static int wpa_supplicant_ctrl_iface_tdls_link_status(
+       struct wpa_supplicant *wpa_s, const char *addr,
+       char *buf, size_t buflen)
+{
+       u8 peer[ETH_ALEN];
+       const char *tdls_status;
+       int ret;
+
+       if (hwaddr_aton(addr, peer)) {
+               wpa_printf(MSG_DEBUG,
+                          "CTRL_IFACE TDLS_LINK_STATUS: Invalid address '%s'",
+                          addr);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS " MACSTR,
+                  MAC2STR(peer));
+
+       tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer);
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS: %s", tdls_status);
+       ret = os_snprintf(buf, buflen, "TDLS link status: %s\n", tdls_status);
+       if (os_snprintf_error(buflen, ret))
+               return -1;
+
+       return ret;
+}
+
 #endif /* CONFIG_TDLS */
 
 
@@ -1728,7 +1775,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
                if (ssid) {
                        u8 *_ssid = ssid->ssid;
                        size_t ssid_len = ssid->ssid_len;
-                       u8 ssid_buf[MAX_SSID_LEN];
+                       u8 ssid_buf[SSID_MAX_LEN];
                        if (ssid_len == 0) {
                                int _res = wpa_drv_get_ssid(wpa_s, ssid_buf);
                                if (_res < 0)
@@ -2537,6 +2584,14 @@ static int wpa_supplicant_ctrl_iface_scan_result(
                pos += ret;
        }
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_FST
+       if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) {
+               ret = os_snprintf(pos, end - pos, "[FST]");
+               if (os_snprintf_error(end - pos, ret))
+                       return -1;
+               pos += ret;
+       }
+#endif /* CONFIG_FST */
 
        ret = os_snprintf(pos, end - pos, "\t%s",
                          wpa_ssid_txt(bss->ssid, bss->ssid_len));
@@ -2848,7 +2903,8 @@ static int wpa_supplicant_ctrl_iface_remove_network(
 #endif /* CONFIG_SME */
                        wpa_sm_set_config(wpa_s->wpa, NULL);
                        eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
-                       wpa_s->own_disconnect_req = 1;
+                       if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+                               wpa_s->own_disconnect_req = 1;
                        wpa_supplicant_deauthenticate(
                                wpa_s, WLAN_REASON_DEAUTH_LEAVING);
                }
@@ -2895,7 +2951,8 @@ static int wpa_supplicant_ctrl_iface_remove_network(
                wpa_sm_set_config(wpa_s->wpa, NULL);
                eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 
-               wpa_s->own_disconnect_req = 1;
+               if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+                       wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
        }
@@ -3047,7 +3104,8 @@ static int wpa_supplicant_ctrl_iface_get_network(
 
 
 static int wpa_supplicant_ctrl_iface_dup_network(
-       struct wpa_supplicant *wpa_s, char *cmd)
+       struct wpa_supplicant *wpa_s, char *cmd,
+       struct wpa_supplicant *dst_wpa_s)
 {
        struct wpa_ssid *ssid_s, *ssid_d;
        char *name, *id, *value;
@@ -3066,8 +3124,10 @@ static int wpa_supplicant_ctrl_iface_dup_network(
 
        id_s = atoi(cmd);
        id_d = atoi(id);
-       wpa_printf(MSG_DEBUG, "CTRL_IFACE: DUP_NETWORK id=%d -> %d name='%s'",
-                  id_s, id_d, name);
+
+       wpa_printf(MSG_DEBUG,
+                  "CTRL_IFACE: DUP_NETWORK ifname=%s->%s id=%d->%d name='%s'",
+                  wpa_s->ifname, dst_wpa_s->ifname, id_s, id_d, name);
 
        ssid_s = wpa_config_get_network(wpa_s->conf, id_s);
        if (ssid_s == NULL) {
@@ -3076,7 +3136,7 @@ static int wpa_supplicant_ctrl_iface_dup_network(
                return -1;
        }
 
-       ssid_d = wpa_config_get_network(wpa_s->conf, id_d);
+       ssid_d = wpa_config_get_network(dst_wpa_s->conf, id_d);
        if (ssid_d == NULL) {
                wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
                           "network id=%d", id_d);
@@ -3090,7 +3150,7 @@ static int wpa_supplicant_ctrl_iface_dup_network(
                return -1;
        }
 
-       ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid_d, name,
+       ret = wpa_supplicant_ctrl_iface_update_network(dst_wpa_s, ssid_d, name,
                                                       value);
 
        os_free(value);
@@ -4253,6 +4313,15 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
                pos += ret;
        }
 
+#ifdef CONFIG_FST
+       if (mask & WPA_BSS_MASK_FST) {
+               ret = fst_ctrl_iface_mb_info(bss->bssid, pos, end - pos);
+               if (ret < 0 || ret >= end - pos)
+                       return 0;
+               pos += ret;
+       }
+#endif /* CONFIG_FST */
+
        if (mask & WPA_BSS_MASK_DELIM) {
                ret = os_snprintf(pos, end - pos, "====\n");
                if (os_snprintf_error(end - pos, ret))
@@ -4565,37 +4634,83 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
        } else
                search_delay = wpas_p2p_search_delay(wpa_s);
 
+       pos = os_strstr(cmd, "freq=");
+       if (pos) {
+               pos += 5;
+               freq = atoi(pos);
+               if (freq <= 0)
+                       return -1;
+       }
+
        /* Must be searched for last, because it adds nul termination */
        pos = os_strstr(cmd, " seek=");
+       if (pos)
+               pos += 6;
        while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) {
                char *term;
 
-               term = os_strchr(pos + 1, ' ');
-               _seek[seek_count++] = pos + 6;
+               _seek[seek_count++] = pos;
                seek = _seek;
-               pos = os_strstr(pos + 6, " seek=");
-
-               if (term)
-                       *term = '\0';
+               term = os_strchr(pos, ' ');
+               if (!term)
+                       break;
+               *term = '\0';
+               pos = os_strstr(term + 1, "seek=");
+               if (pos)
+                       pos += 5;
        }
        if (seek_count > P2P_MAX_QUERY_HASH) {
                seek[0] = NULL;
                seek_count = 1;
        }
 
-       pos = os_strstr(cmd, "freq=");
-       if (pos) {
-               pos += 5;
-               freq = atoi(pos);
-               if (freq <= 0)
-                       return -1;
-       }
-
        return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type,
                             _dev_id, search_delay, seek_count, seek, freq);
 }
 
 
+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;
@@ -4604,6 +4719,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) {
@@ -4662,6 +4778,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) {
@@ -5157,6 +5285,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)
@@ -5229,6 +5359,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;
@@ -5246,7 +5389,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);
 }
 
 
@@ -5471,13 +5614,10 @@ static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd)
 
 
 static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
-                                        char *cmd, int freq, int ht40,
-                                        int vht)
+                                        int id, int freq, int ht40, int vht)
 {
-       int id;
        struct wpa_ssid *ssid;
 
-       id = atoi(cmd);
        ssid = wpa_config_get_network(wpa_s->conf, id);
        if (ssid == NULL || ssid->disabled != 2) {
                wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
@@ -5493,31 +5633,35 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
 
 static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
 {
-       int freq = 0, ht40, vht;
-       char *pos;
+       int freq = 0, persistent = 0, group_id = -1;
+       int vht = wpa_s->conf->p2p_go_vht;
+       int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+       char *token, *context = NULL;
 
-       pos = os_strstr(cmd, "freq=");
-       if (pos)
-               freq = atoi(pos + 5);
+       while ((token = str_token(cmd, " ", &context))) {
+               if (sscanf(token, "freq=%d", &freq) == 1 ||
+                   sscanf(token, "persistent=%d", &group_id) == 1) {
+                       continue;
+               } else if (os_strcmp(token, "ht40") == 0) {
+                       ht40 = 1;
+               } else if (os_strcmp(token, "vht") == 0) {
+                       vht = 1;
+                       ht40 = 1;
+               } else if (os_strcmp(token, "persistent") == 0) {
+                       persistent = 1;
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "CTRL: Invalid P2P_GROUP_ADD parameter: '%s'",
+                                  token);
+                       return -1;
+               }
+       }
 
-       vht = (os_strstr(cmd, "vht") != NULL) || wpa_s->conf->p2p_go_vht;
-       ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
-               vht;
+       if (group_id >= 0)
+               return p2p_ctrl_group_add_persistent(wpa_s, group_id,
+                                                    freq, ht40, vht);
 
-       if (os_strncmp(cmd, "persistent=", 11) == 0)
-               return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq,
-                                                    ht40, vht);
-       if (os_strcmp(cmd, "persistent") == 0 ||
-           os_strncmp(cmd, "persistent ", 11) == 0)
-               return wpas_p2p_group_add(wpa_s, 1, freq, ht40, vht);
-       if (os_strncmp(cmd, "freq=", 5) == 0)
-               return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht);
-       if (ht40)
-               return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht);
-
-       wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'",
-                  cmd);
-       return -1;
+       return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht);
 }
 
 
@@ -6954,6 +7098,8 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params,
        void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
                                 struct wpa_scan_results *scan_res);
        int *manual_scan_freqs = NULL;
+       struct wpa_ssid_value *ssid = NULL, *ns;
+       unsigned int ssid_count = 0;
 
        if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
                *reply_len = -1;
@@ -7008,6 +7154,60 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params,
                        *reply_len = -1;
                        goto done;
                }
+
+               pos = params;
+               while (pos && *pos != '\0') {
+                       if (os_strncmp(pos, "ssid ", 5) == 0) {
+                               char *end;
+
+                               pos += 5;
+                               end = pos;
+                               while (*end) {
+                                       if (*end == '\0' || *end == ' ')
+                                               break;
+                                       end++;
+                               }
+
+                               ns = os_realloc_array(
+                                       ssid, ssid_count + 1,
+                                       sizeof(struct wpa_ssid_value));
+                               if (ns == NULL) {
+                                       *reply_len = -1;
+                                       goto done;
+                               }
+                               ssid = ns;
+
+                               if ((end - pos) & 0x01 ||
+                                   end - pos > 2 * SSID_MAX_LEN ||
+                                   hexstr2bin(pos, ssid[ssid_count].ssid,
+                                              (end - pos) / 2) < 0) {
+                                       wpa_printf(MSG_DEBUG,
+                                                  "Invalid SSID value '%s'",
+                                                  pos);
+                                       *reply_len = -1;
+                                       goto done;
+                               }
+                               ssid[ssid_count].ssid_len = (end - pos) / 2;
+                               wpa_hexdump_ascii(MSG_DEBUG, "scan SSID",
+                                                 ssid[ssid_count].ssid,
+                                                 ssid[ssid_count].ssid_len);
+                               ssid_count++;
+                               pos = end;
+                       }
+
+                       pos = os_strchr(pos, ' ');
+                       if (pos)
+                               pos++;
+               }
+       }
+
+       wpa_s->num_ssids_from_scan_req = ssid_count;
+       os_free(wpa_s->ssids_from_scan_req);
+       if (ssid_count) {
+               wpa_s->ssids_from_scan_req = ssid;
+               ssid = NULL;
+       } else {
+               wpa_s->ssids_from_scan_req = NULL;
        }
 
        if (scan_only)
@@ -7071,6 +7271,7 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params,
 
 done:
        os_free(manual_scan_freqs);
+       os_free(ssid);
 }
 
 
@@ -7272,7 +7473,7 @@ void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
 {
        struct wpa_supplicant *wpa_s = ctx;
        const struct ether_header *eth;
-       const struct iphdr *ip;
+       struct iphdr ip;
        const u8 *pos;
        unsigned int i;
 
@@ -7280,14 +7481,14 @@ void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
                return;
 
        eth = (const struct ether_header *) buf;
-       ip = (const struct iphdr *) (eth + 1);
-       pos = (const u8 *) (ip + 1);
+       os_memcpy(&ip, eth + 1, sizeof(ip));
+       pos = &buf[sizeof(*eth) + sizeof(ip)];
 
-       if (ip->ihl != 5 || ip->version != 4 ||
-           ntohs(ip->tot_len) != HWSIM_IP_LEN)
+       if (ip.ihl != 5 || ip.version != 4 ||
+           ntohs(ip.tot_len) != HWSIM_IP_LEN)
                return;
 
-       for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) {
+       for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) {
                if (*pos != (u8) i)
                        return;
                pos++;
@@ -7334,7 +7535,7 @@ static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd)
        int used;
        long int val;
        u8 tos;
-       u8 buf[HWSIM_PACKETLEN];
+       u8 buf[2 + HWSIM_PACKETLEN];
        struct ether_header *eth;
        struct iphdr *ip;
        u8 *dpos;
@@ -7362,7 +7563,7 @@ static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd)
                return -1;
        tos = val;
 
-       eth = (struct ether_header *) buf;
+       eth = (struct ether_header *) &buf[2];
        os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
        os_memcpy(eth->ether_shost, src, ETH_ALEN);
        eth->ether_type = htons(ETHERTYPE_IP);
@@ -7374,14 +7575,14 @@ static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd)
        ip->tos = tos;
        ip->tot_len = htons(HWSIM_IP_LEN);
        ip->protocol = 1;
-       ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
-       ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2);
+       ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
+       ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
        ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
        dpos = (u8 *) (ip + 1);
        for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++)
                *dpos++ = i;
 
-       if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, buf,
+       if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2],
                           HWSIM_PACKETLEN) < 0)
                return -1;
 
@@ -7470,6 +7671,44 @@ static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s,
 #endif /* WPA_TRACE_BFD */
 }
 
+
+static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, 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);
+       pos = os_strchr(cmd, ':');
+       if (pos) {
+               pos++;
+               os_strlcpy(wpa_trace_test_fail_func, pos,
+                          sizeof(wpa_trace_test_fail_func));
+       } else {
+               wpa_trace_test_fail_after = 0;
+       }
+       return 0;
+#else /* WPA_TRACE_BFD */
+       return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s,
+                                   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 */
+       return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
@@ -7706,7 +7945,7 @@ static int wpas_ctrl_iface_send_neigbor_rep(struct wpa_supplicant *wpa_s,
 
        if (os_strncmp(cmd, " ssid=", 6) == 0) {
                ssid.ssid_len = os_strlen(cmd + 6);
-               if (ssid.ssid_len > 32)
+               if (ssid.ssid_len > SSID_MAX_LEN)
                        return -1;
                ssid.ssid = (u8 *) (cmd + 6);
                ssid_p = &ssid;
@@ -8107,7 +8346,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                if (wpas_p2p_group_remove(wpa_s, buf + 17))
                        reply_len = -1;
        } else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) {
-               if (wpas_p2p_group_add(wpa_s, 0, 0, 0, 0))
+               if (p2p_ctrl_group_add(wpa_s, ""))
                        reply_len = -1;
        } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
                if (p2p_ctrl_group_add(wpa_s, buf + 14))
@@ -8311,7 +8550,8 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                reply_len = wpa_supplicant_ctrl_iface_get_network(
                        wpa_s, buf + 12, reply, reply_size);
        } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
-               if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12))
+               if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12,
+                                                         wpa_s))
                        reply_len = -1;
        } else if (os_strcmp(buf, "LIST_CREDS") == 0) {
                reply_len = wpa_supplicant_ctrl_iface_list_creds(
@@ -8414,6 +8654,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s,
                                                                      buf + 24))
                        reply_len = -1;
+       } else if (os_strncmp(buf, "TDLS_LINK_STATUS ", 17) == 0) {
+               reply_len = wpa_supplicant_ctrl_iface_tdls_link_status(
+                       wpa_s, buf + 17, reply, reply_size);
 #endif /* CONFIG_TDLS */
        } else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) {
                reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size);
@@ -8484,6 +8727,11 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                        reply_len = -1;
        } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
                reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size);
+       } else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
+               if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0)
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "GET_FAIL") == 0) {
+               reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size);
 #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)
@@ -8593,10 +8841,17 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global,
                pos = os_strchr(pos, '\t');
                if (pos)
                        *pos++ = '\0';
+               if (!extra[0])
+                       break;
+
                if (os_strcmp(extra, "create") == 0)
                        create_iface = 1;
-               else
+               else {
+                       wpa_printf(MSG_DEBUG,
+                                  "INTERFACE_ADD unsupported extra parameter: '%s'",
+                                  extra);
                        return -1;
+               }
        } while (0);
 
        if (create_iface) {
@@ -8678,7 +8933,7 @@ static int wpa_supplicant_global_iface_list(struct wpa_global *global,
        char *pos, *end;
 
        for (i = 0; wpa_drivers[i]; i++) {
-               struct wpa_driver_ops *drv = wpa_drivers[i];
+               const struct wpa_driver_ops *drv = wpa_drivers[i];
                if (drv->get_interfaces == NULL)
                        continue;
                tmp = drv->get_interfaces(global->drv_priv[i]);
@@ -8896,6 +9151,41 @@ static int wpas_global_ctrl_iface_set(struct wpa_global *global, char *cmd)
 }
 
 
+static int wpas_global_ctrl_iface_dup_network(struct wpa_global *global,
+                                             char *cmd)
+{
+       struct wpa_supplicant *wpa_s[2]; /* src, dst */
+       char *p;
+       unsigned int i;
+
+       /* cmd: "<src ifname> <dst ifname> <src network id> <dst network id>
+        * <variable name> */
+
+       for (i = 0; i < ARRAY_SIZE(wpa_s) ; i++) {
+               p = os_strchr(cmd, ' ');
+               if (p == NULL)
+                       return -1;
+               *p = '\0';
+
+               wpa_s[i] = global->ifaces;
+               for (; wpa_s[i]; wpa_s[i] = wpa_s[i]->next) {
+                       if (os_strcmp(cmd, wpa_s[i]->ifname) == 0)
+                               break;
+               }
+
+               if (!wpa_s[i]) {
+                       wpa_printf(MSG_DEBUG,
+                                  "CTRL_IFACE: Could not find iface=%s", cmd);
+                       return -1;
+               }
+
+               cmd = p + 1;
+       }
+
+       return wpa_supplicant_ctrl_iface_dup_network(wpa_s[0], cmd, wpa_s[1]);
+}
+
+
 #ifndef CONFIG_NO_CONFIG_WRITE
 static int wpas_global_ctrl_iface_save_config(struct wpa_global *global)
 {
@@ -8977,6 +9267,59 @@ static int wpas_global_ctrl_iface_status(struct wpa_global *global,
 }
 
 
+#ifdef CONFIG_FST
+
+static int wpas_global_ctrl_iface_fst_attach(struct wpa_global *global,
+                                            char *cmd, char *buf,
+                                            size_t reply_size)
+{
+       char ifname[IFNAMSIZ + 1];
+       struct fst_iface_cfg cfg;
+       struct wpa_supplicant *wpa_s;
+       struct fst_wpa_obj iface_obj;
+
+       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);
+                       if (wpa_s->fst)
+                               return os_snprintf(buf, reply_size, "OK\n");
+               }
+       }
+
+       return -1;
+}
+
+
+static int wpas_global_ctrl_iface_fst_detach(struct wpa_global *global,
+                                            char *cmd, char *buf,
+                                            size_t reply_size)
+{
+       char ifname[IFNAMSIZ + 1];
+       struct wpa_supplicant *wpa_s;
+
+       if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
+               wpa_s = wpa_supplicant_get_iface(global, ifname);
+               if (wpa_s) {
+                       if (!fst_iface_detach(ifname)) {
+                               wpa_s->fst = NULL;
+                               return os_snprintf(buf, reply_size, "OK\n");
+                       }
+               }
+       }
+
+       return -1;
+}
+
+#endif /* CONFIG_FST */
+
+
 char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
                                                char *buf, size_t *resp_len)
 {
@@ -9028,6 +9371,18 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
        } else if (os_strcmp(buf, "INTERFACES") == 0) {
                reply_len = wpa_supplicant_global_iface_interfaces(
                        global, reply, reply_size);
+#ifdef CONFIG_FST
+       } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
+               reply_len = wpas_global_ctrl_iface_fst_attach(global, buf + 11,
+                                                             reply,
+                                                             reply_size);
+       } else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
+               reply_len = wpas_global_ctrl_iface_fst_detach(global, buf + 11,
+                                                             reply,
+                                                             reply_size);
+       } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
+               reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
+#endif /* CONFIG_FST */
        } else if (os_strcmp(buf, "TERMINATE") == 0) {
                wpa_supplicant_terminate_proc(global);
        } else if (os_strcmp(buf, "SUSPEND") == 0) {
@@ -9048,6 +9403,9 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
 #endif /* CONFIG_P2P */
                        reply_len = -1;
                }
+       } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
+               if (wpas_global_ctrl_iface_dup_network(global, buf + 12))
+                       reply_len = -1;
 #ifndef CONFIG_NO_CONFIG_WRITE
        } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
                if (wpas_global_ctrl_iface_save_config(global))